diff --git a/crates/acp_thread/src/mention.rs b/crates/acp_thread/src/mention.rs index d29a5c9af0..61e613a3c8 100644 --- a/crates/acp_thread/src/mention.rs +++ b/crates/acp_thread/src/mention.rs @@ -1,4 +1,3 @@ -use agent_client_protocol as acp; use anyhow::{Result, bail}; use std::path::PathBuf; @@ -6,7 +5,8 @@ use std::path::PathBuf; pub enum MentionUri { File(PathBuf), Symbol(PathBuf, String), - Thread(acp::SessionId), + Thread(String), + TextThread(PathBuf), Rule(String), } @@ -24,7 +24,7 @@ impl MentionUri { } "zed" => { if let Some(thread) = path.strip_prefix("/agent/thread/") { - Ok(Self::Thread(acp::SessionId(thread.into()))) + Ok(Self::Thread(thread.into())) } else if let Some(rule) = path.strip_prefix("/agent/rule/") { Ok(Self::Rule(rule.into())) } else { @@ -40,6 +40,7 @@ impl MentionUri { MentionUri::File(path) => path.file_name().unwrap().to_string_lossy().into_owned(), MentionUri::Symbol(_path, name) => name.clone(), MentionUri::Thread(thread) => thread.to_string(), + MentionUri::TextThread(thread) => thread.display().to_string(), MentionUri::Rule(rule) => rule.clone(), } } @@ -60,7 +61,10 @@ impl MentionUri { format!("file://{}#{}", path.display(), name) } MentionUri::Thread(thread) => { - format!("zed:///agent/thread/{}", thread.0) + format!("zed:///agent/thread/{}", thread) + } + MentionUri::TextThread(path) => { + format!("zed:///agent/text-thread/{}", path.display()) } MentionUri::Rule(rule) => { format!("zed:///agent/rule/{}", rule) @@ -100,7 +104,7 @@ mod tests { let thread_uri = "zed:///agent/thread/session123"; let parsed = MentionUri::parse(thread_uri).unwrap(); match &parsed { - MentionUri::Thread(session_id) => assert_eq!(session_id.0.as_ref(), "session123"), + MentionUri::Thread(thread_id) => assert_eq!(thread_id, "session123"), _ => panic!("Expected Thread variant"), } assert_eq!(parsed.to_uri(), thread_uri); diff --git a/crates/agent2/src/thread.rs b/crates/agent2/src/thread.rs index 678e4cb5d2..eea129cc0c 100644 --- a/crates/agent2/src/thread.rs +++ b/crates/agent2/src/thread.rs @@ -1196,6 +1196,9 @@ impl AgentMessage { MentionUri::Thread(_session_id) => { write!(&mut thread_context, "\n{}\n", content).ok(); } + MentionUri::TextThread(_session_id) => { + write!(&mut thread_context, "\n{}\n", content).ok(); + } MentionUri::Rule(_user_prompt_id) => { write!( &mut rules_context, diff --git a/crates/agent_ui/src/acp/completion_provider.rs b/crates/agent_ui/src/acp/completion_provider.rs index ec32ce249d..090df753c1 100644 --- a/crates/agent_ui/src/acp/completion_provider.rs +++ b/crates/agent_ui/src/acp/completion_provider.rs @@ -306,8 +306,8 @@ fn search( pub struct ContextPickerCompletionProvider { mention_set: Arc>, workspace: WeakEntity, - thread_store: Option>, - text_thread_store: Option>, + thread_store: WeakEntity, + text_thread_store: WeakEntity, editor: WeakEntity, } @@ -315,8 +315,8 @@ impl ContextPickerCompletionProvider { pub fn new( mention_set: Arc>, workspace: WeakEntity, - thread_store: Option>, - text_thread_store: Option>, + thread_store: WeakEntity, + text_thread_store: WeakEntity, editor: WeakEntity, ) -> Self { Self { @@ -474,77 +474,39 @@ impl ContextPickerCompletionProvider { recent: bool, editor: Entity, mention_set: Arc>, - thread_store: Entity, - text_thread_store: Entity, ) -> Completion { - todo!(); - // let icon_for_completion = if recent { - // IconName::HistoryRerun - // } else { - // IconName::Thread - // }; + let icon_for_completion = if recent { + IconName::HistoryRerun + } else { + IconName::Thread + }; - // let new_text = format!("{} ", MentionUri::Thread(thread_id)); + let uri = match &thread_entry { + ThreadContextEntry::Thread { id, .. } => MentionUri::Thread(id.to_string()), + ThreadContextEntry::Context { path, .. } => MentionUri::TextThread(path.to_path_buf()), + }; + let new_text = format!("{} ", uri.to_link()); - // let new_text_len = new_text.len(); - // Completion { - // replace_range: source_range.clone(), - // new_text, - // label: CodeLabel::plain(thread_entry.title().to_string(), None), - // documentation: None, - // insert_text_mode: None, - // source: project::CompletionSource::Custom, - // icon_path: Some(icon_for_completion.path().into()), - // confirm: Some(confirm_completion_callback( - // IconName::Thread.path().into(), - // thread_entry.title().clone(), - // excerpt_id, - // source_range.start, - // new_text_len - 1, - // editor.clone(), - // context_store.clone(), - // move |window, cx| match &thread_entry { - // ThreadContextEntry::Thread { id, .. } => { - // let thread_id = id.clone(); - // let context_store = context_store.clone(); - // let thread_store = thread_store.clone(); - // window.spawn::<_, Option<_>>(cx, async move |cx| { - // let thread: Entity = thread_store - // .update_in(cx, |thread_store, window, cx| { - // thread_store.open_thread(&thread_id, window, cx) - // }) - // .ok()? - // .await - // .log_err()?; - // let context = context_store - // .update(cx, |context_store, cx| { - // context_store.add_thread(thread, false, cx) - // }) - // .ok()??; - // Some(context) - // }) - // } - // ThreadContextEntry::Context { path, .. } => { - // let path = path.clone(); - // let context_store = context_store.clone(); - // let text_thread_store = text_thread_store.clone(); - // cx.spawn::<_, Option<_>>(async move |cx| { - // let thread = text_thread_store - // .update(cx, |store, cx| store.open_local_context(path, cx)) - // .ok()? - // .await - // .log_err()?; - // let context = context_store - // .update(cx, |context_store, cx| { - // context_store.add_text_thread(thread, false, cx) - // }) - // .ok()??; - // Some(context) - // }) - // } - // }, - // )), - // } + let new_text_len = new_text.len(); + Completion { + replace_range: source_range.clone(), + new_text, + label: CodeLabel::plain(thread_entry.title().to_string(), None), + documentation: None, + insert_text_mode: None, + source: project::CompletionSource::Custom, + icon_path: Some(icon_for_completion.path().into()), + confirm: Some(confirm_completion_callback( + IconName::Thread.path().into(), + thread_entry.title().clone(), + excerpt_id, + source_range.start, + new_text_len - 1, + editor.clone(), + mention_set, + uri, + )), + } } fn completion_for_rules( @@ -555,7 +517,7 @@ impl ContextPickerCompletionProvider { mention_set: Arc>, ) -> Completion { let uri = MentionUri::Rule(rules.prompt_id.0.to_string()); - let new_text = uri.to_link(); + let new_text = format!("{} ", uri.to_link()); let new_text_len = new_text.len(); Completion { replace_range: source_range.clone(), @@ -769,7 +731,7 @@ impl CompletionProvider for ContextPickerCompletionProvider { excluded_paths.insert(path.clone()); } MentionUri::Thread(thread) => { - excluded_threads.insert(thread.0.as_ref().into()); + excluded_threads.insert(thread.as_str().into()); } _ => {} } @@ -850,21 +812,14 @@ impl CompletionProvider for ContextPickerCompletionProvider { Match::Thread(ThreadMatch { thread, is_recent, .. - }) => { - let thread_store = thread_store.as_ref().and_then(|t| t.upgrade())?; - let text_thread_store = - text_thread_store.as_ref().and_then(|t| t.upgrade())?; - Some(Self::completion_for_thread( - thread, - excerpt_id, - source_range.clone(), - is_recent, - editor.clone(), - mention_set.clone(), - thread_store, - text_thread_store, - )) - } + }) => Some(Self::completion_for_thread( + thread, + excerpt_id, + source_range.clone(), + is_recent, + editor.clone(), + mention_set.clone(), + )), Match::Rules(user_rules) => Some(Self::completion_for_rules( user_rules, diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index c72f29343b..937c5d1f5e 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -4,6 +4,7 @@ use acp_thread::{ }; use acp_thread::{AgentConnection, Plan}; use action_log::ActionLog; +use agent::{TextThreadStore, ThreadStore}; use agent_client_protocol as acp; use agent_servers::AgentServer; use agent_settings::{AgentSettings, NotifyWhenAgentWaiting}; @@ -104,6 +105,8 @@ impl AcpThreadView { agent: Rc, workspace: WeakEntity, project: Entity, + thread_store: WeakEntity, + text_thread_store: WeaksEntity, message_history: Rc>>>, min_lines: usize, max_lines: Option, @@ -141,11 +144,9 @@ impl AcpThreadView { editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new( mention_set.clone(), workspace.clone(), - // todo! provide thread stores - None, - None, + thread_store.clone(), + text_thread_store.clone(), cx.weak_entity(), - // None, )))); editor.set_context_menu_options(ContextMenuOptions { min_entries_visible: 12, @@ -3579,6 +3580,8 @@ mod tests { Rc::new(agent), workspace.downgrade(), project, + WeakEntity::new_invalid(), + WeakEntity::new_invalid(), Rc::new(RefCell::new(MessageHistory::default())), 1, None, diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 87e4dd822c..01e88c3bfa 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -924,6 +924,9 @@ impl AgentPanel { agent: crate::ExternalAgent, } + let thread_store = self.thread_store.clone(); + let text_thread_store = self.context_store.clone(); + cx.spawn_in(window, async move |this, cx| { let server: Rc = match agent_choice { Some(agent) => { @@ -962,6 +965,8 @@ impl AgentPanel { server, workspace.clone(), project, + thread_store.clone(), + text_thread_store.clone(), message_history, MIN_EDITOR_LINES, Some(MAX_EDITOR_LINES),