@@ -13,7 +13,7 @@ path = "src/agent_ui.rs"
doctest = false
[features]
-test-support = ["assistant_text_thread/test-support", "acp_thread/test-support", "eval_utils", "gpui/test-support", "language/test-support", "reqwest_client", "workspace/test-support", "agent/test-support"]
+test-support = ["assistant_text_thread/test-support", "acp_thread/test-support", "eval_utils", "gpui/test-support", "language/test-support", "reqwest_client", "workspace/test-support", "agent/test-support", "rules_library/test-support"]
unit-eval = []
[dependencies]
@@ -6903,6 +6903,9 @@ impl Render for AcpThreadView {
v_flex()
.size_full()
.key_context("AcpThread")
+ .on_action(cx.listener(|this, _: &menu::Cancel, _, cx| {
+ this.cancel_generation(cx);
+ }))
.on_action(cx.listener(Self::toggle_burn_mode))
.on_action(cx.listener(Self::keep_all))
.on_action(cx.listener(Self::reject_all))
@@ -8224,6 +8227,140 @@ pub(crate) mod tests {
});
}
+ struct GeneratingThreadSetup {
+ thread_view: Entity<AcpThreadView>,
+ thread: Entity<AcpThread>,
+ message_editor: Entity<MessageEditor>,
+ }
+
+ async fn setup_generating_thread(
+ cx: &mut TestAppContext,
+ ) -> (GeneratingThreadSetup, &mut VisualTestContext) {
+ let connection = StubAgentConnection::new();
+
+ let (thread_view, cx) =
+ setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
+ add_to_workspace(thread_view.clone(), cx);
+
+ let message_editor = cx.read(|cx| thread_view.read(cx).message_editor.clone());
+ message_editor.update_in(cx, |editor, window, cx| {
+ editor.set_text("Hello", window, cx);
+ });
+ thread_view.update_in(cx, |thread_view, window, cx| {
+ thread_view.send(window, cx);
+ });
+
+ let (thread, session_id) = thread_view.read_with(cx, |view, cx| {
+ let thread = view.thread().unwrap();
+ (thread.clone(), thread.read(cx).session_id().clone())
+ });
+
+ cx.run_until_parked();
+
+ cx.update(|_, cx| {
+ connection.send_update(
+ session_id.clone(),
+ acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new(
+ "Response chunk".into(),
+ )),
+ cx,
+ );
+ });
+
+ cx.run_until_parked();
+
+ thread.read_with(cx, |thread, _cx| {
+ assert_eq!(thread.status(), ThreadStatus::Generating);
+ });
+
+ (
+ GeneratingThreadSetup {
+ thread_view,
+ thread,
+ message_editor,
+ },
+ cx,
+ )
+ }
+
+ #[gpui::test]
+ async fn test_escape_cancels_generation_from_conversation_focus(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let (setup, cx) = setup_generating_thread(cx).await;
+
+ let focus_handle = setup
+ .thread_view
+ .read_with(cx, |view, _cx| view.focus_handle.clone());
+ cx.update(|window, cx| {
+ window.focus(&focus_handle, cx);
+ });
+
+ setup.thread_view.update_in(cx, |_, window, cx| {
+ window.dispatch_action(menu::Cancel.boxed_clone(), cx);
+ });
+
+ cx.run_until_parked();
+
+ setup.thread.read_with(cx, |thread, _cx| {
+ assert_eq!(thread.status(), ThreadStatus::Idle);
+ });
+ }
+
+ #[gpui::test]
+ async fn test_escape_cancels_generation_from_editor_focus(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let (setup, cx) = setup_generating_thread(cx).await;
+
+ let editor_focus_handle = setup
+ .message_editor
+ .read_with(cx, |editor, cx| editor.focus_handle(cx));
+ cx.update(|window, cx| {
+ window.focus(&editor_focus_handle, cx);
+ });
+
+ setup.message_editor.update_in(cx, |_, window, cx| {
+ window.dispatch_action(editor::actions::Cancel.boxed_clone(), cx);
+ });
+
+ cx.run_until_parked();
+
+ setup.thread.read_with(cx, |thread, _cx| {
+ assert_eq!(thread.status(), ThreadStatus::Idle);
+ });
+ }
+
+ #[gpui::test]
+ async fn test_escape_when_idle_is_noop(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let (thread_view, cx) =
+ setup_thread_view(StubAgentServer::new(StubAgentConnection::new()), cx).await;
+ add_to_workspace(thread_view.clone(), cx);
+
+ let thread = thread_view.read_with(cx, |view, _cx| view.thread().unwrap().clone());
+
+ thread.read_with(cx, |thread, _cx| {
+ assert_eq!(thread.status(), ThreadStatus::Idle);
+ });
+
+ let focus_handle = thread_view.read_with(cx, |view, _cx| view.focus_handle.clone());
+ cx.update(|window, cx| {
+ window.focus(&focus_handle, cx);
+ });
+
+ thread_view.update_in(cx, |_, window, cx| {
+ window.dispatch_action(menu::Cancel.boxed_clone(), cx);
+ });
+
+ cx.run_until_parked();
+
+ thread.read_with(cx, |thread, _cx| {
+ assert_eq!(thread.status(), ThreadStatus::Idle);
+ });
+ }
+
#[gpui::test]
async fn test_interrupt(cx: &mut TestAppContext) {
init_test(cx);