agent: Add a whitespace after inserting @-mention to allow for continuous typing (#30381)

Patrick Leibersperger , Peter Tripp , and Danilo Leal created

Release Notes:

- agent: Added a space after @-mentioning something in the message
editor to allow for continuous typing.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/agent/src/context_picker.rs                     |  1 
crates/agent/src/context_picker/completion_provider.rs | 45 ++++++-----
2 files changed, 25 insertions(+), 21 deletions(-)

Detailed changes

crates/agent/src/context_picker.rs 🔗

@@ -766,6 +766,7 @@ pub(crate) fn insert_crease_for_mention(
 
         let ids = editor.insert_creases(vec![crease.clone()], cx);
         editor.fold_creases(vec![crease], false, window, cx);
+
         Some(ids[0])
     })
 }

crates/agent/src/context_picker/completion_provider.rs 🔗

@@ -322,7 +322,10 @@ impl ContextPickerCompletionProvider {
                             })
                             .collect::<Vec<_>>();
 
-                        let new_text = selection_infos.iter().map(|(_, link, _)| link).join(" ");
+                        let new_text = format!(
+                            "{} ",
+                            selection_infos.iter().map(|(_, link, _)| link).join(" ")
+                        );
 
                         let callback = Arc::new({
                             let context_store = context_store.clone();
@@ -420,7 +423,7 @@ impl ContextPickerCompletionProvider {
         } else {
             IconName::MessageBubbles
         };
-        let new_text = MentionLink::for_thread(&thread_entry);
+        let new_text = format!("{} ", MentionLink::for_thread(&thread_entry));
         let new_text_len = new_text.len();
         Completion {
             replace_range: source_range.clone(),
@@ -435,7 +438,7 @@ impl ContextPickerCompletionProvider {
                 thread_entry.title().clone(),
                 excerpt_id,
                 source_range.start,
-                new_text_len,
+                new_text_len - 1,
                 editor.clone(),
                 context_store.clone(),
                 move |window, cx| match &thread_entry {
@@ -489,7 +492,7 @@ impl ContextPickerCompletionProvider {
         editor: Entity<Editor>,
         context_store: Entity<ContextStore>,
     ) -> Completion {
-        let new_text = MentionLink::for_rule(&rules);
+        let new_text = format!("{} ", MentionLink::for_rule(&rules));
         let new_text_len = new_text.len();
         Completion {
             replace_range: source_range.clone(),
@@ -504,7 +507,7 @@ impl ContextPickerCompletionProvider {
                 rules.title.clone(),
                 excerpt_id,
                 source_range.start,
-                new_text_len,
+                new_text_len - 1,
                 editor.clone(),
                 context_store.clone(),
                 move |_, cx| {
@@ -526,7 +529,7 @@ impl ContextPickerCompletionProvider {
         context_store: Entity<ContextStore>,
         http_client: Arc<HttpClientWithUrl>,
     ) -> Completion {
-        let new_text = MentionLink::for_fetch(&url_to_fetch);
+        let new_text = format!("{} ", MentionLink::for_fetch(&url_to_fetch));
         let new_text_len = new_text.len();
         Completion {
             replace_range: source_range.clone(),
@@ -541,7 +544,7 @@ impl ContextPickerCompletionProvider {
                 url_to_fetch.clone(),
                 excerpt_id,
                 source_range.start,
-                new_text_len,
+                new_text_len - 1,
                 editor.clone(),
                 context_store.clone(),
                 move |_, cx| {
@@ -611,7 +614,7 @@ impl ContextPickerCompletionProvider {
             crease_icon_path.clone()
         };
 
-        let new_text = MentionLink::for_file(&file_name, &full_path);
+        let new_text = format!("{} ", MentionLink::for_file(&file_name, &full_path));
         let new_text_len = new_text.len();
         Completion {
             replace_range: source_range.clone(),
@@ -626,7 +629,7 @@ impl ContextPickerCompletionProvider {
                 file_name,
                 excerpt_id,
                 source_range.start,
-                new_text_len,
+                new_text_len - 1,
                 editor,
                 context_store.clone(),
                 move |_, cx| {
@@ -682,7 +685,7 @@ impl ContextPickerCompletionProvider {
         label.push_str(" ", None);
         label.push_str(&file_name, comment_id);
 
-        let new_text = MentionLink::for_symbol(&symbol.name, &full_path);
+        let new_text = format!("{} ", MentionLink::for_symbol(&symbol.name, &full_path));
         let new_text_len = new_text.len();
         Some(Completion {
             replace_range: source_range.clone(),
@@ -697,7 +700,7 @@ impl ContextPickerCompletionProvider {
                 symbol.name.clone().into(),
                 excerpt_id,
                 source_range.start,
-                new_text_len,
+                new_text_len - 1,
                 editor.clone(),
                 context_store.clone(),
                 move |_, cx| {
@@ -1353,7 +1356,7 @@ mod tests {
         });
 
         editor.update(&mut cx, |editor, cx| {
-            assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt)",);
+            assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) ");
             assert!(!editor.has_visible_completions_menu());
             assert_eq!(
                 fold_ranges(editor, cx),
@@ -1364,7 +1367,7 @@ mod tests {
         cx.simulate_input(" ");
 
         editor.update(&mut cx, |editor, cx| {
-            assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) ",);
+            assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt)  ");
             assert!(!editor.has_visible_completions_menu());
             assert_eq!(
                 fold_ranges(editor, cx),
@@ -1377,7 +1380,7 @@ mod tests {
         editor.update(&mut cx, |editor, cx| {
             assert_eq!(
                 editor.text(cx),
-                "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum ",
+                "Lorem [@one.txt](@file:dir/a/one.txt)  Ipsum ",
             );
             assert!(!editor.has_visible_completions_menu());
             assert_eq!(
@@ -1391,7 +1394,7 @@ mod tests {
         editor.update(&mut cx, |editor, cx| {
             assert_eq!(
                 editor.text(cx),
-                "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum @file ",
+                "Lorem [@one.txt](@file:dir/a/one.txt)  Ipsum @file ",
             );
             assert!(editor.has_visible_completions_menu());
             assert_eq!(
@@ -1409,14 +1412,14 @@ mod tests {
         editor.update(&mut cx, |editor, cx| {
             assert_eq!(
                 editor.text(cx),
-                "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)"
+                "Lorem [@one.txt](@file:dir/a/one.txt)  Ipsum [@seven.txt](@file:dir/b/seven.txt) "
             );
             assert!(!editor.has_visible_completions_menu());
             assert_eq!(
                 fold_ranges(editor, cx),
                 vec![
                     Point::new(0, 6)..Point::new(0, 37),
-                    Point::new(0, 44)..Point::new(0, 79)
+                    Point::new(0, 45)..Point::new(0, 80)
                 ]
             );
         });
@@ -1426,14 +1429,14 @@ mod tests {
         editor.update(&mut cx, |editor, cx| {
             assert_eq!(
                 editor.text(cx),
-                "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)\n@"
+                "Lorem [@one.txt](@file:dir/a/one.txt)  Ipsum [@seven.txt](@file:dir/b/seven.txt) \n@"
             );
             assert!(editor.has_visible_completions_menu());
             assert_eq!(
                 fold_ranges(editor, cx),
                 vec![
                     Point::new(0, 6)..Point::new(0, 37),
-                    Point::new(0, 44)..Point::new(0, 79)
+                    Point::new(0, 45)..Point::new(0, 80)
                 ]
             );
         });
@@ -1447,14 +1450,14 @@ mod tests {
         editor.update(&mut cx, |editor, cx| {
             assert_eq!(
                 editor.text(cx),
-                "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)\n[@six.txt](@file:dir/b/six.txt)"
+                "Lorem [@one.txt](@file:dir/a/one.txt)  Ipsum [@seven.txt](@file:dir/b/seven.txt) \n[@six.txt](@file:dir/b/six.txt) "
             );
             assert!(!editor.has_visible_completions_menu());
             assert_eq!(
                 fold_ranges(editor, cx),
                 vec![
                     Point::new(0, 6)..Point::new(0, 37),
-                    Point::new(0, 44)..Point::new(0, 79),
+                    Point::new(0, 45)..Point::new(0, 80),
                     Point::new(1, 0)..Point::new(1, 31)
                 ]
             );