agent_ui: Improve message editor history navigation (#35532)

- We no longer move through history if a message has been edited by the
user -
It is possible to navigate back down to an empty message

Co-authored-by: Cole Miller <cole@zed.dev>

Release Notes:

- N/A

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Ben Brandt 2025-08-02 23:49:22 +02:00 committed by GitHub
parent f4391ed631
commit 986e3e7cbc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -31,7 +31,7 @@ use markdown::{HeadingLevelStyles, Markdown, MarkdownElement, MarkdownStyle};
use parking_lot::Mutex; use parking_lot::Mutex;
use project::Project; use project::Project;
use settings::Settings as _; use settings::Settings as _;
use text::Anchor; use text::{Anchor, BufferSnapshot};
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{Disclosure, Divider, DividerColor, KeyBinding, Tooltip, prelude::*}; use ui::{Disclosure, Divider, DividerColor, KeyBinding, Tooltip, prelude::*};
use util::ResultExt; use util::ResultExt;
@ -61,7 +61,7 @@ pub struct AcpThreadView {
thread_state: ThreadState, thread_state: ThreadState,
diff_editors: HashMap<EntityId, Entity<Editor>>, diff_editors: HashMap<EntityId, Entity<Editor>>,
message_editor: Entity<Editor>, message_editor: Entity<Editor>,
message_set_from_history: bool, message_set_from_history: Option<BufferSnapshot>,
_message_editor_subscription: Subscription, _message_editor_subscription: Subscription,
mention_set: Arc<Mutex<MentionSet>>, mention_set: Arc<Mutex<MentionSet>>,
notifications: Vec<WindowHandle<AgentNotification>>, notifications: Vec<WindowHandle<AgentNotification>>,
@ -144,12 +144,26 @@ impl AcpThreadView {
editor editor
}); });
let message_editor_subscription = cx.subscribe(&message_editor, |this, _, event, _| { let message_editor_subscription =
cx.subscribe(&message_editor, |this, editor, event, cx| {
if let editor::EditorEvent::BufferEdited = &event { if let editor::EditorEvent::BufferEdited = &event {
if !this.message_set_from_history { let buffer = editor
.read(cx)
.buffer()
.read(cx)
.as_singleton()
.unwrap()
.read(cx)
.snapshot();
if let Some(message) = this.message_set_from_history.clone()
&& message.version() != buffer.version()
{
this.message_set_from_history = None;
}
if this.message_set_from_history.is_none() {
this.message_history.borrow_mut().reset_position(); this.message_history.borrow_mut().reset_position();
} }
this.message_set_from_history = false;
} }
}); });
@ -178,7 +192,7 @@ impl AcpThreadView {
project: project.clone(), project: project.clone(),
thread_state: Self::initial_state(agent, workspace, project, window, cx), thread_state: Self::initial_state(agent, workspace, project, window, cx),
message_editor, message_editor,
message_set_from_history: false, message_set_from_history: None,
_message_editor_subscription: message_editor_subscription, _message_editor_subscription: message_editor_subscription,
mention_set, mention_set,
notifications: Vec::new(), notifications: Vec::new(),
@ -424,11 +438,21 @@ impl AcpThreadView {
window: &mut Window, window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
if self.message_set_from_history.is_none() && !self.message_editor.read(cx).is_empty(cx) {
self.message_editor.update(cx, |editor, cx| {
editor.move_up(&Default::default(), window, cx);
});
return;
}
self.message_set_from_history = Self::set_draft_message( self.message_set_from_history = Self::set_draft_message(
self.message_editor.clone(), self.message_editor.clone(),
self.mention_set.clone(), self.mention_set.clone(),
self.project.clone(), self.project.clone(),
self.message_history.borrow_mut().prev(), self.message_history
.borrow_mut()
.prev()
.map(|blocks| blocks.as_slice()),
window, window,
cx, cx,
); );
@ -440,14 +464,35 @@ impl AcpThreadView {
window: &mut Window, window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
self.message_set_from_history = Self::set_draft_message( if self.message_set_from_history.is_none() {
self.message_editor.update(cx, |editor, cx| {
editor.move_down(&Default::default(), window, cx);
});
return;
}
let mut message_history = self.message_history.borrow_mut();
let next_history = message_history.next();
let set_draft_message = Self::set_draft_message(
self.message_editor.clone(), self.message_editor.clone(),
self.mention_set.clone(), self.mention_set.clone(),
self.project.clone(), self.project.clone(),
self.message_history.borrow_mut().next(), Some(
next_history
.map(|blocks| blocks.as_slice())
.unwrap_or_else(|| &[]),
),
window, window,
cx, cx,
); );
// If we reset the text to an empty string because we ran out of history,
// we don't want to mark it as coming from the history
self.message_set_from_history = if next_history.is_some() {
set_draft_message
} else {
None
};
} }
fn open_agent_diff(&mut self, _: &OpenAgentDiff, window: &mut Window, cx: &mut Context<Self>) { fn open_agent_diff(&mut self, _: &OpenAgentDiff, window: &mut Window, cx: &mut Context<Self>) {
@ -481,15 +526,13 @@ impl AcpThreadView {
message_editor: Entity<Editor>, message_editor: Entity<Editor>,
mention_set: Arc<Mutex<MentionSet>>, mention_set: Arc<Mutex<MentionSet>>,
project: Entity<Project>, project: Entity<Project>,
message: Option<&Vec<acp::ContentBlock>>, message: Option<&[acp::ContentBlock]>,
window: &mut Window, window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> bool { ) -> Option<BufferSnapshot> {
cx.notify(); cx.notify();
let Some(message) = message else { let message = message?;
return false;
};
let mut text = String::new(); let mut text = String::new();
let mut mentions = Vec::new(); let mut mentions = Vec::new();
@ -553,7 +596,8 @@ impl AcpThreadView {
} }
} }
true let snapshot = snapshot.as_singleton().unwrap().2.clone();
Some(snapshot.text)
} }
fn handle_thread_event( fn handle_thread_event(