Add `agent: chat with follow` action (experimental) (#31401)

Joseph T. Lyons created

This PR introduces a new `agent: chat with follow` action that
automatically enables "Follow Agent" when submitting a chat message with
`cmd-enter` or `ctrl-enter`. This is experimental. I'm not super
thrilled with the name, but the root action to submit a chat is called
`agent: chat`, so I'm following that wording. I'm also unsure if the
binding feels right or not.

Release Notes:

- Added an `agent: chat with follow` action via `cmd-enter` on macOS and
`ctrl-enter` on Linux

Change summary

assets/keymaps/default-linux.json  |  1 +
assets/keymaps/default-macos.json  |  1 +
crates/agent/src/agent.rs          |  1 +
crates/agent/src/message_editor.rs | 21 +++++++++++++++++++--
4 files changed, 22 insertions(+), 2 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -274,6 +274,7 @@
     "context": "MessageEditor > Editor",
     "bindings": {
       "enter": "agent::Chat",
+      "ctrl-enter": "agent::ChatWithFollow",
       "ctrl-i": "agent::ToggleProfileSelector",
       "shift-ctrl-r": "agent::OpenAgentDiff"
     }

assets/keymaps/default-macos.json 🔗

@@ -311,6 +311,7 @@
     "use_key_equivalents": true,
     "bindings": {
       "enter": "agent::Chat",
+      "cmd-enter": "agent::ChatWithFollow",
       "cmd-i": "agent::ToggleProfileSelector",
       "shift-ctrl-r": "agent::OpenAgentDiff"
     }

crates/agent/src/agent.rs 🔗

@@ -69,6 +69,7 @@ actions!(
         AddContextServer,
         RemoveSelectedThread,
         Chat,
+        ChatWithFollow,
         CycleNextInlineAssist,
         CyclePreviousInlineAssist,
         FocusUp,

crates/agent/src/message_editor.rs 🔗

@@ -49,8 +49,9 @@ use crate::profile_selector::ProfileSelector;
 use crate::thread::{MessageCrease, Thread, TokenUsageRatio};
 use crate::thread_store::{TextThreadStore, ThreadStore};
 use crate::{
-    ActiveThread, AgentDiffPane, Chat, ExpandMessageEditor, Follow, NewThread, OpenAgentDiff,
-    RemoveAllContext, ToggleContextPicker, ToggleProfileSelector, register_agent_preview,
+    ActiveThread, AgentDiffPane, Chat, ChatWithFollow, ExpandMessageEditor, Follow, NewThread,
+    OpenAgentDiff, RemoveAllContext, ToggleContextPicker, ToggleProfileSelector,
+    register_agent_preview,
 };
 
 #[derive(RegisterComponent)]
@@ -302,6 +303,21 @@ impl MessageEditor {
         cx.notify();
     }
 
+    fn chat_with_follow(
+        &mut self,
+        _: &ChatWithFollow,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        self.workspace
+            .update(cx, |this, cx| {
+                this.follow(CollaboratorId::Agent, window, cx)
+            })
+            .log_err();
+
+        self.chat(&Chat, window, cx);
+    }
+
     fn is_editor_empty(&self, cx: &App) -> bool {
         self.editor.read(cx).text(cx).trim().is_empty()
     }
@@ -562,6 +578,7 @@ impl MessageEditor {
         v_flex()
             .key_context("MessageEditor")
             .on_action(cx.listener(Self::chat))
+            .on_action(cx.listener(Self::chat_with_follow))
             .on_action(cx.listener(|this, _: &ToggleProfileSelector, window, cx| {
                 this.profile_selector
                     .read(cx)