Threads and rule support
Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
parent
e2973998ad
commit
91e22597a8
5 changed files with 68 additions and 98 deletions
|
@ -1,4 +1,3 @@
|
||||||
use agent_client_protocol as acp;
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -6,7 +5,8 @@ use std::path::PathBuf;
|
||||||
pub enum MentionUri {
|
pub enum MentionUri {
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
Symbol(PathBuf, String),
|
Symbol(PathBuf, String),
|
||||||
Thread(acp::SessionId),
|
Thread(String),
|
||||||
|
TextThread(PathBuf),
|
||||||
Rule(String),
|
Rule(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ impl MentionUri {
|
||||||
}
|
}
|
||||||
"zed" => {
|
"zed" => {
|
||||||
if let Some(thread) = path.strip_prefix("/agent/thread/") {
|
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/") {
|
} else if let Some(rule) = path.strip_prefix("/agent/rule/") {
|
||||||
Ok(Self::Rule(rule.into()))
|
Ok(Self::Rule(rule.into()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,6 +40,7 @@ impl MentionUri {
|
||||||
MentionUri::File(path) => path.file_name().unwrap().to_string_lossy().into_owned(),
|
MentionUri::File(path) => path.file_name().unwrap().to_string_lossy().into_owned(),
|
||||||
MentionUri::Symbol(_path, name) => name.clone(),
|
MentionUri::Symbol(_path, name) => name.clone(),
|
||||||
MentionUri::Thread(thread) => thread.to_string(),
|
MentionUri::Thread(thread) => thread.to_string(),
|
||||||
|
MentionUri::TextThread(thread) => thread.display().to_string(),
|
||||||
MentionUri::Rule(rule) => rule.clone(),
|
MentionUri::Rule(rule) => rule.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +61,10 @@ impl MentionUri {
|
||||||
format!("file://{}#{}", path.display(), name)
|
format!("file://{}#{}", path.display(), name)
|
||||||
}
|
}
|
||||||
MentionUri::Thread(thread) => {
|
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) => {
|
MentionUri::Rule(rule) => {
|
||||||
format!("zed:///agent/rule/{}", rule)
|
format!("zed:///agent/rule/{}", rule)
|
||||||
|
@ -100,7 +104,7 @@ mod tests {
|
||||||
let thread_uri = "zed:///agent/thread/session123";
|
let thread_uri = "zed:///agent/thread/session123";
|
||||||
let parsed = MentionUri::parse(thread_uri).unwrap();
|
let parsed = MentionUri::parse(thread_uri).unwrap();
|
||||||
match &parsed {
|
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"),
|
_ => panic!("Expected Thread variant"),
|
||||||
}
|
}
|
||||||
assert_eq!(parsed.to_uri(), thread_uri);
|
assert_eq!(parsed.to_uri(), thread_uri);
|
||||||
|
|
|
@ -1196,6 +1196,9 @@ impl AgentMessage {
|
||||||
MentionUri::Thread(_session_id) => {
|
MentionUri::Thread(_session_id) => {
|
||||||
write!(&mut thread_context, "\n{}\n", content).ok();
|
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) => {
|
MentionUri::Rule(_user_prompt_id) => {
|
||||||
write!(
|
write!(
|
||||||
&mut rules_context,
|
&mut rules_context,
|
||||||
|
|
|
@ -306,8 +306,8 @@ fn search(
|
||||||
pub struct ContextPickerCompletionProvider {
|
pub struct ContextPickerCompletionProvider {
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
thread_store: Option<WeakEntity<ThreadStore>>,
|
thread_store: WeakEntity<ThreadStore>,
|
||||||
text_thread_store: Option<WeakEntity<TextThreadStore>>,
|
text_thread_store: WeakEntity<TextThreadStore>,
|
||||||
editor: WeakEntity<Editor>,
|
editor: WeakEntity<Editor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,8 +315,8 @@ impl ContextPickerCompletionProvider {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
thread_store: Option<WeakEntity<ThreadStore>>,
|
thread_store: WeakEntity<ThreadStore>,
|
||||||
text_thread_store: Option<WeakEntity<TextThreadStore>>,
|
text_thread_store: WeakEntity<TextThreadStore>,
|
||||||
editor: WeakEntity<Editor>,
|
editor: WeakEntity<Editor>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -474,77 +474,39 @@ impl ContextPickerCompletionProvider {
|
||||||
recent: bool,
|
recent: bool,
|
||||||
editor: Entity<Editor>,
|
editor: Entity<Editor>,
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
thread_store: Entity<ThreadStore>,
|
|
||||||
text_thread_store: Entity<TextThreadStore>,
|
|
||||||
) -> Completion {
|
) -> Completion {
|
||||||
todo!();
|
let icon_for_completion = if recent {
|
||||||
// let icon_for_completion = if recent {
|
IconName::HistoryRerun
|
||||||
// IconName::HistoryRerun
|
} else {
|
||||||
// } else {
|
IconName::Thread
|
||||||
// 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();
|
let new_text_len = new_text.len();
|
||||||
// Completion {
|
Completion {
|
||||||
// replace_range: source_range.clone(),
|
replace_range: source_range.clone(),
|
||||||
// new_text,
|
new_text,
|
||||||
// label: CodeLabel::plain(thread_entry.title().to_string(), None),
|
label: CodeLabel::plain(thread_entry.title().to_string(), None),
|
||||||
// documentation: None,
|
documentation: None,
|
||||||
// insert_text_mode: None,
|
insert_text_mode: None,
|
||||||
// source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
// icon_path: Some(icon_for_completion.path().into()),
|
icon_path: Some(icon_for_completion.path().into()),
|
||||||
// confirm: Some(confirm_completion_callback(
|
confirm: Some(confirm_completion_callback(
|
||||||
// IconName::Thread.path().into(),
|
IconName::Thread.path().into(),
|
||||||
// thread_entry.title().clone(),
|
thread_entry.title().clone(),
|
||||||
// excerpt_id,
|
excerpt_id,
|
||||||
// source_range.start,
|
source_range.start,
|
||||||
// new_text_len - 1,
|
new_text_len - 1,
|
||||||
// editor.clone(),
|
editor.clone(),
|
||||||
// context_store.clone(),
|
mention_set,
|
||||||
// move |window, cx| match &thread_entry {
|
uri,
|
||||||
// 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> = 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)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// )),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_for_rules(
|
fn completion_for_rules(
|
||||||
|
@ -555,7 +517,7 @@ impl ContextPickerCompletionProvider {
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
) -> Completion {
|
) -> Completion {
|
||||||
let uri = MentionUri::Rule(rules.prompt_id.0.to_string());
|
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();
|
let new_text_len = new_text.len();
|
||||||
Completion {
|
Completion {
|
||||||
replace_range: source_range.clone(),
|
replace_range: source_range.clone(),
|
||||||
|
@ -769,7 +731,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
|
||||||
excluded_paths.insert(path.clone());
|
excluded_paths.insert(path.clone());
|
||||||
}
|
}
|
||||||
MentionUri::Thread(thread) => {
|
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 {
|
Match::Thread(ThreadMatch {
|
||||||
thread, is_recent, ..
|
thread, is_recent, ..
|
||||||
}) => {
|
}) => Some(Self::completion_for_thread(
|
||||||
let thread_store = thread_store.as_ref().and_then(|t| t.upgrade())?;
|
thread,
|
||||||
let text_thread_store =
|
excerpt_id,
|
||||||
text_thread_store.as_ref().and_then(|t| t.upgrade())?;
|
source_range.clone(),
|
||||||
Some(Self::completion_for_thread(
|
is_recent,
|
||||||
thread,
|
editor.clone(),
|
||||||
excerpt_id,
|
mention_set.clone(),
|
||||||
source_range.clone(),
|
)),
|
||||||
is_recent,
|
|
||||||
editor.clone(),
|
|
||||||
mention_set.clone(),
|
|
||||||
thread_store,
|
|
||||||
text_thread_store,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
Match::Rules(user_rules) => Some(Self::completion_for_rules(
|
Match::Rules(user_rules) => Some(Self::completion_for_rules(
|
||||||
user_rules,
|
user_rules,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use acp_thread::{
|
||||||
};
|
};
|
||||||
use acp_thread::{AgentConnection, Plan};
|
use acp_thread::{AgentConnection, Plan};
|
||||||
use action_log::ActionLog;
|
use action_log::ActionLog;
|
||||||
|
use agent::{TextThreadStore, ThreadStore};
|
||||||
use agent_client_protocol as acp;
|
use agent_client_protocol as acp;
|
||||||
use agent_servers::AgentServer;
|
use agent_servers::AgentServer;
|
||||||
use agent_settings::{AgentSettings, NotifyWhenAgentWaiting};
|
use agent_settings::{AgentSettings, NotifyWhenAgentWaiting};
|
||||||
|
@ -104,6 +105,8 @@ impl AcpThreadView {
|
||||||
agent: Rc<dyn AgentServer>,
|
agent: Rc<dyn AgentServer>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
|
thread_store: WeakEntity<ThreadStore>,
|
||||||
|
text_thread_store: WeaksEntity<TextThreadStore>,
|
||||||
message_history: Rc<RefCell<MessageHistory<Vec<acp::ContentBlock>>>>,
|
message_history: Rc<RefCell<MessageHistory<Vec<acp::ContentBlock>>>>,
|
||||||
min_lines: usize,
|
min_lines: usize,
|
||||||
max_lines: Option<usize>,
|
max_lines: Option<usize>,
|
||||||
|
@ -141,11 +144,9 @@ impl AcpThreadView {
|
||||||
editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new(
|
editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new(
|
||||||
mention_set.clone(),
|
mention_set.clone(),
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
// todo! provide thread stores
|
thread_store.clone(),
|
||||||
None,
|
text_thread_store.clone(),
|
||||||
None,
|
|
||||||
cx.weak_entity(),
|
cx.weak_entity(),
|
||||||
// None,
|
|
||||||
))));
|
))));
|
||||||
editor.set_context_menu_options(ContextMenuOptions {
|
editor.set_context_menu_options(ContextMenuOptions {
|
||||||
min_entries_visible: 12,
|
min_entries_visible: 12,
|
||||||
|
@ -3579,6 +3580,8 @@ mod tests {
|
||||||
Rc::new(agent),
|
Rc::new(agent),
|
||||||
workspace.downgrade(),
|
workspace.downgrade(),
|
||||||
project,
|
project,
|
||||||
|
WeakEntity::new_invalid(),
|
||||||
|
WeakEntity::new_invalid(),
|
||||||
Rc::new(RefCell::new(MessageHistory::default())),
|
Rc::new(RefCell::new(MessageHistory::default())),
|
||||||
1,
|
1,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -924,6 +924,9 @@ impl AgentPanel {
|
||||||
agent: crate::ExternalAgent,
|
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| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let server: Rc<dyn AgentServer> = match agent_choice {
|
let server: Rc<dyn AgentServer> = match agent_choice {
|
||||||
Some(agent) => {
|
Some(agent) => {
|
||||||
|
@ -962,6 +965,8 @@ impl AgentPanel {
|
||||||
server,
|
server,
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
project,
|
project,
|
||||||
|
thread_store.clone(),
|
||||||
|
text_thread_store.clone(),
|
||||||
message_history,
|
message_history,
|
||||||
MIN_EDITOR_LINES,
|
MIN_EDITOR_LINES,
|
||||||
Some(MAX_EDITOR_LINES),
|
Some(MAX_EDITOR_LINES),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue