From 6ed2155b9a5d194da4fd276160fb94722f48da7f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 13 Aug 2025 10:54:24 -0300 Subject: [PATCH] Replace to_link with as_link Co-authored-by: Cole Miller --- crates/acp_thread/src/acp_thread.rs | 2 +- crates/acp_thread/src/mention.rs | 85 ++++++++++--------- crates/agent2/src/thread.rs | 4 +- .../agent_ui/src/acp/completion_provider.rs | 8 +- crates/agent_ui/src/acp/thread_view.rs | 6 +- crates/prompt_store/src/prompt_store.rs | 1 - 6 files changed, 57 insertions(+), 49 deletions(-) diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index eccbef96b8..d853686020 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -341,7 +341,7 @@ impl ContentBlock { .. }) => { if let Some(uri) = MentionUri::parse(&uri).log_err() { - uri.to_link() + uri.as_link().to_string() } else { uri.clone() } diff --git a/crates/acp_thread/src/mention.rs b/crates/acp_thread/src/mention.rs index 7a1b161c6c..0f9c8ad36b 100644 --- a/crates/acp_thread/src/mention.rs +++ b/crates/acp_thread/src/mention.rs @@ -2,6 +2,7 @@ use agent::ThreadId; use anyhow::{Context as _, Result, bail}; use prompt_store::{PromptId, UserPromptId}; use std::{ + fmt, ops::Range, path::{Path, PathBuf}, }; @@ -108,7 +109,6 @@ impl MentionUri { .to_string_lossy() .into_owned(), MentionUri::Symbol { name, .. } => name.clone(), - // todo! better names MentionUri::Thread { name, .. } => name.clone(), MentionUri::TextThread { name, .. } => name.clone(), MentionUri::Rule { name, .. } => name.clone(), @@ -118,63 +118,72 @@ impl MentionUri { } } - // todo! return something that implements display to avoid extra allocs - pub fn to_link(&self) -> String { - let name = self.name(); - let uri = self.to_uri(); - format!("[{name}]({uri})") + pub fn as_link<'a>(&'a self) -> MentionLink<'a> { + MentionLink(self) } - pub fn to_uri(&self) -> String { + pub fn to_uri(&self) -> Url { match self { MentionUri::File(path) => { - format!("file://{}", path.display()) + let mut url = Url::parse("file:///").unwrap(); + url.set_path(&path.to_string_lossy()); + url } MentionUri::Symbol { path, name, line_range, } => { - let query = url::form_urlencoded::Serializer::new(String::new()) - .append_pair("symbol", name) - .finish(); - format!( - "file://{}?{query}#L{}:{}", - path.display(), + let mut url = Url::parse("file:///").unwrap(); + url.set_path(&path.to_string_lossy()); + url.query_pairs_mut().append_pair("symbol", name); + url.set_fragment(Some(&format!( + "L{}:{}", line_range.start + 1, - line_range.end + 1, - ) + line_range.end + 1 + ))); + url } MentionUri::Selection { path, line_range } => { - format!( - "file://{}#L{}:{}", - path.display(), + let mut url = Url::parse("file:///").unwrap(); + url.set_path(&path.to_string_lossy()); + url.set_fragment(Some(&format!( + "L{}:{}", line_range.start + 1, - line_range.end + 1, - ) + line_range.end + 1 + ))); + url } MentionUri::Thread { name, id } => { - let query = url::form_urlencoded::Serializer::new(String::new()) - .append_pair("name", name) - .finish(); - format!("zed:///agent/thread/{id}?{query}") + let mut url = Url::parse("zed:///").unwrap(); + url.set_path(&format!("/agent/thread/{id}")); + url.query_pairs_mut().append_pair("name", name); + url } MentionUri::TextThread { path, name } => { - let query = url::form_urlencoded::Serializer::new(String::new()) - .append_pair("name", name) - .finish(); - format!("zed:///agent/text-thread/{}?{query}", path.display()) + let mut url = Url::parse("zed:///").unwrap(); + url.set_path(&format!("/agent/text-thread/{}", path.to_string_lossy())); + url.query_pairs_mut().append_pair("name", name); + url } MentionUri::Rule { name, id } => { - let query = url::form_urlencoded::Serializer::new(String::new()) - .append_pair("name", name) - .finish(); - format!("zed:///agent/rule/{id}?{query}") + let mut url = Url::parse("zed:///").unwrap(); + url.set_path(&format!("/agent/rule/{id}")); + url.query_pairs_mut().append_pair("name", name); + url } } } } +pub struct MentionLink<'a>(&'a MentionUri); + +impl fmt::Display for MentionLink<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}]({})", self.0.name(), self.0.to_uri()) + } +} + fn single_query_param(url: &Url, name: &'static str) -> Result> { let pairs = url.query_pairs().collect::>(); match pairs.as_slice() { @@ -211,7 +220,7 @@ mod tests { MentionUri::File(path) => assert_eq!(path.to_str().unwrap(), "/path/to/file.rs"), _ => panic!("Expected File variant"), } - assert_eq!(parsed.to_uri(), file_uri); + assert_eq!(parsed.to_uri().to_string(), file_uri); } #[test] @@ -231,7 +240,7 @@ mod tests { } _ => panic!("Expected Symbol variant"), } - assert_eq!(parsed.to_uri(), symbol_uri); + assert_eq!(parsed.to_uri().to_string(), symbol_uri); } #[test] @@ -246,7 +255,7 @@ mod tests { } _ => panic!("Expected Selection variant"), } - assert_eq!(parsed.to_uri(), selection_uri); + assert_eq!(parsed.to_uri().to_string(), selection_uri); } #[test] @@ -263,7 +272,7 @@ mod tests { } _ => panic!("Expected Thread variant"), } - assert_eq!(parsed.to_uri(), thread_uri); + assert_eq!(parsed.to_uri().to_string(), thread_uri); } #[test] @@ -277,7 +286,7 @@ mod tests { } _ => panic!("Expected Rule variant"), } - assert_eq!(parsed.to_uri(), rule_uri); + assert_eq!(parsed.to_uri().to_string(), rule_uri); } #[test] diff --git a/crates/agent2/src/thread.rs b/crates/agent2/src/thread.rs index 7a15074e35..246df2cac5 100644 --- a/crates/agent2/src/thread.rs +++ b/crates/agent2/src/thread.rs @@ -113,7 +113,7 @@ impl AgentMessage { } } MessageContent::Mention { uri, .. } => { - write!(markdown, "{}", uri.to_link()).ok(); + write!(markdown, "{}", uri.as_link()).ok(); } } } @@ -1228,7 +1228,7 @@ impl AgentMessage { } } - language_model::MessageContent::Text(uri.to_link()) + language_model::MessageContent::Text(uri.as_link().to_string()) } }; diff --git a/crates/agent_ui/src/acp/completion_provider.rs b/crates/agent_ui/src/acp/completion_provider.rs index a8566634f4..519be85b0a 100644 --- a/crates/agent_ui/src/acp/completion_provider.rs +++ b/crates/agent_ui/src/acp/completion_provider.rs @@ -555,7 +555,7 @@ impl ContextPickerCompletionProvider { name: title.to_string(), }, }; - let new_text = format!("{} ", uri.to_link()); + let new_text = format!("{} ", uri.as_link()); let new_text_len = new_text.len(); Completion { @@ -590,7 +590,7 @@ impl ContextPickerCompletionProvider { id: rule.prompt_id.into(), name: rule.title.to_string(), }; - let new_text = format!("{} ", uri.to_link()); + let new_text = format!("{} ", uri.as_link()); let new_text_len = new_text.len(); Completion { replace_range: source_range.clone(), @@ -656,7 +656,7 @@ impl ContextPickerCompletionProvider { }; let file_uri = MentionUri::File(abs_path.into()); - let new_text = format!("{} ", file_uri.to_link()); + let new_text = format!("{} ", file_uri.as_link()); let new_text_len = new_text.len(); Some(Completion { replace_range: source_range.clone(), @@ -698,7 +698,7 @@ impl ContextPickerCompletionProvider { name: symbol.name.clone(), line_range: symbol.range.start.0.row..symbol.range.end.0.row, }; - let new_text = format!("{} ", uri.to_link()); + let new_text = format!("{} ", uri.as_link()); let new_text_len = new_text.len(); Some(Completion { replace_range: source_range.clone(), diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 4663037b81..df2ae07574 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -32,6 +32,7 @@ use project::{CompletionIntent, Project}; use prompt_store::PromptId; use rope::Point; use settings::{Settings as _, SettingsStore}; +use std::fmt::Write as _; use std::path::PathBuf; use std::{ cell::RefCell, collections::BTreeMap, path::Path, process::ExitStatus, rc::Rc, sync::Arc, @@ -439,7 +440,7 @@ impl AcpThreadView { acp::TextResourceContents { mime_type: None, text: mention.content.clone(), - uri: mention.uri.to_uri(), + uri: mention.uri.to_uri().to_string(), }, ), })); @@ -614,8 +615,7 @@ impl AcpThreadView { let path = PathBuf::from(&resource.uri); let project_path = project.read(cx).project_path_for_absolute_path(&path, cx); let start = text.len(); - let content = MentionUri::File(path).to_uri(); - text.push_str(&content); + let _ = write!(&mut text, "{}", MentionUri::File(path).to_uri()); let end = text.len(); if let Some(project_path) = project_path { let filename: SharedString = project_path diff --git a/crates/prompt_store/src/prompt_store.rs b/crates/prompt_store/src/prompt_store.rs index 510b16f815..06a65b97cd 100644 --- a/crates/prompt_store/src/prompt_store.rs +++ b/crates/prompt_store/src/prompt_store.rs @@ -90,7 +90,6 @@ impl From for UserPromptId { } } -// todo! remove me impl std::fmt::Display for PromptId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self {