Handle fetch completions

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Agus Zubiaga 2025-08-13 13:42:39 -03:00
parent 2f6c9e3a2b
commit 46ed71fb9a
2 changed files with 91 additions and 26 deletions

View file

@ -1229,15 +1229,7 @@ impl AgentMessage {
.ok();
}
MentionUri::Fetch { url } => {
write!(
&mut fetch_context,
"\n{}",
MarkdownCodeBlock {
tag: &format!("md {url}"),
text: &content
}
)
.ok();
write!(&mut fetch_context, "\nFetch: {}\n\n{}", url, content).ok();
}
}

View file

@ -26,12 +26,14 @@ use text::{Anchor, OffsetRangeExt as _, ToPoint as _};
use ui::prelude::*;
use url::Url;
use workspace::Workspace;
use workspace::notifications::NotifyResultExt;
use agent::{
context::RULES_ICON,
thread_store::{TextThreadStore, ThreadStore},
};
use crate::context_picker::fetch_context_picker::fetch_url_content;
use crate::context_picker::file_context_picker::{FileMatch, search_files};
use crate::context_picker::rules_context_picker::{RulesContextEntry, search_rules};
use crate::context_picker::symbol_context_picker::SymbolMatch;
@ -55,6 +57,10 @@ impl MentionSet {
self.uri_by_crease_id.insert(crease_id, uri);
}
pub fn add_fetch_result(&mut self, url: Url, content: String) {
self.fetch_results.insert(url, content);
}
pub fn drain(&mut self) -> impl Iterator<Item = CreaseId> {
self.uri_by_crease_id.drain().map(|(id, _)| id)
}
@ -279,8 +285,11 @@ fn search(
}
Some(ContextPickerMode::Fetch) => {
// todo! make a new mode type for acp?
unreachable!()
if !query.is_empty() {
Task::ready(vec![Match::Fetch(query.into())])
} else {
Task::ready(Vec::new())
}
}
Some(ContextPickerMode::Rules) => {
@ -750,9 +759,7 @@ impl ContextPickerCompletionProvider {
mention_set: Arc<Mutex<MentionSet>>,
http_client: Arc<HttpClientWithUrl>,
) -> Option<Completion> {
let url = url::Url::parse(url_to_fetch.as_ref()).ok()?;
let mention_uri = MentionUri::Fetch { url };
let new_text = format!("{} ", mention_uri.as_link());
let new_text = format!("@fetch {} ", url_to_fetch.clone());
let new_text_len = new_text.len();
Some(Completion {
replace_range: source_range.clone(),
@ -762,17 +769,82 @@ impl ContextPickerCompletionProvider {
source: project::CompletionSource::Custom,
icon_path: Some(IconName::ToolWeb.path().into()),
insert_text_mode: None,
// todo! custom callback to fetch
confirm: Some(confirm_completion_callback(
IconName::ToolWeb.path().into(),
url_to_fetch.clone(),
excerpt_id,
source_range.start,
new_text_len - 1,
editor.clone(),
mention_set,
mention_uri,
)),
confirm: Some({
let start = source_range.start;
let content_len = new_text_len - 1;
let editor = editor.clone();
let url_to_fetch = url_to_fetch.clone();
let source_range = source_range.clone();
Arc::new(move |_, window, cx| {
let Some(url) = url::Url::parse(url_to_fetch.as_ref())
.or_else(|_| url::Url::parse(&format!("https://{url_to_fetch}")))
.notify_app_err(cx)
else {
return false;
};
let mention_uri = MentionUri::Fetch { url: url.clone() };
let editor = editor.clone();
let mention_set = mention_set.clone();
let http_client = http_client.clone();
let source_range = source_range.clone();
window.defer(cx, move |window, cx| {
let url = url.clone();
let Some(crease_id) = crate::context_picker::insert_crease_for_mention(
excerpt_id,
start,
content_len,
url.to_string().into(),
IconName::ToolWeb.path().into(),
editor.clone(),
window,
cx,
) else {
return;
};
let editor = editor.clone();
let mention_set = mention_set.clone();
let http_client = http_client.clone();
let source_range = source_range.clone();
window
.spawn(cx, async move |cx| {
if let Some(content) =
fetch_url_content(http_client, url.to_string())
.await
.notify_async_err(cx)
{
mention_set.lock().add_fetch_result(url, content);
mention_set.lock().insert(crease_id, mention_uri.clone());
} else {
// Remove crease if we failed to fetch
editor
.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let Some(anchor) = snapshot
.anchor_in_excerpt(excerpt_id, source_range.start)
else {
return;
};
editor.display_map.update(cx, |display_map, cx| {
display_map.unfold_intersecting(
vec![anchor..anchor],
true,
cx,
);
});
editor.remove_creases([crease_id], cx);
})
.ok();
}
Some(())
})
.detach();
});
false
})
}),
})
}
}
@ -820,6 +892,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
};
let project = workspace.read(cx).project().clone();
let http_client = workspace.read(cx).client().http_client();
let snapshot = buffer.read(cx).snapshot();
let source_range = snapshot.anchor_before(state.source_range.start)
..snapshot.anchor_after(state.source_range.end);
@ -945,7 +1018,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
excerpt_id,
editor.clone(),
mention_set.clone(),
todo!(),
http_client.clone(),
),
Match::Entry(EntryMatch { entry, .. }) => Self::completion_for_entry(