agent: Cancel pending in-edit user message upon new message submit (#29565)

Danilo Leal created

Previously, if you clicked on a user message to edit it, and then, while
the user message has the editor pending, sent a new message via the
textarea, the whole thread would be grayed out because we hadn't
dismissed the to-be-edited pending user message. That's now fixed.

Release Notes:

- agent: Fixed a bug that would make the whole thread be grayed out upon
sending a new message while a user message had a pending edit.

Change summary

crates/agent/src/active_thread.rs  | 5 +++++
crates/agent/src/message_editor.rs | 8 ++++++++
crates/agent/src/thread.rs         | 9 +++++++++
crates/eval/src/example.rs         | 3 ++-
4 files changed, 24 insertions(+), 1 deletion(-)

Detailed changes

crates/agent/src/active_thread.rs 🔗

@@ -903,6 +903,11 @@ impl ActiveThread {
         cx: &mut Context<Self>,
     ) {
         match event {
+            ThreadEvent::CancelEditing => {
+                if self.editing_message.is_some() {
+                    self.cancel_editing_message(&menu::Cancel, window, cx);
+                }
+            }
             ThreadEvent::ShowError(error) => {
                 self.last_error = Some(error.clone());
             }

crates/agent/src/message_editor.rs 🔗

@@ -244,6 +244,10 @@ impl MessageEditor {
             return;
         }
 
+        self.thread.update(cx, |thread, cx| {
+            thread.cancel_editing(cx);
+        });
+
         if self.thread.read(cx).is_generating() {
             self.stop_current_and_send_new_message(window, cx);
             return;
@@ -312,6 +316,10 @@ impl MessageEditor {
     }
 
     fn stop_current_and_send_new_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.thread.update(cx, |thread, cx| {
+            thread.cancel_editing(cx);
+        });
+
         let cancelled = self.thread.update(cx, |thread, cx| {
             thread.cancel_last_completion(Some(window.window_handle()), cx)
         });

crates/agent/src/thread.rs 🔗

@@ -1866,6 +1866,14 @@ impl Thread {
         canceled
     }
 
+    /// Signals that any in-progress editing should be canceled.
+    ///
+    /// This method is used to notify listeners (like ActiveThread) that
+    /// they should cancel any editing operations.
+    pub fn cancel_editing(&mut self, cx: &mut Context<Self>) {
+        cx.emit(ThreadEvent::CancelEditing);
+    }
+
     pub fn feedback(&self) -> Option<ThreadFeedback> {
         self.feedback
     }
@@ -2384,6 +2392,7 @@ pub enum ThreadEvent {
     },
     CheckpointChanged,
     ToolConfirmationNeeded,
+    CancelEditing,
 }
 
 impl EventEmitter<ThreadEvent> for Thread {}

crates/eval/src/example.rs 🔗

@@ -276,7 +276,8 @@ impl ExampleContext {
                 | ThreadEvent::ReceivedTextChunk
                 | ThreadEvent::StreamedToolUse { .. }
                 | ThreadEvent::CheckpointChanged
-                | ThreadEvent::UsageUpdated(_) => {
+                | ThreadEvent::UsageUpdated(_)
+                | ThreadEvent::CancelEditing => {
                     tx.try_send(Ok(())).ok();
                     if std::env::var("ZED_EVAL_DEBUG").is_ok() {
                         println!("{}Event: {:#?}", log_prefix, event);