Separate actions for accepting the inline suggestions and completions (#12094)

Raphael LΓΌthy and Conrad Irvin created

Release Notes:
- Added `editor::AcceptInlineCompletion` action (bound to Tab by
default) for accepting inline completions. ([6788](https://github.com/zed-industries/zed/issues/6788))

---------

Signed-off-by: Raphael LΓΌthy <raphael.luethy@fhnw.ch>
Co-authored-by: Conrad Irvin <conrad@zed.dev>

Change summary

assets/keymaps/default-linux.json                 |  6 ++
assets/keymaps/default-macos.json                 |  6 ++
crates/copilot/src/copilot_completion_provider.rs |  8 +-
crates/editor/src/actions.rs                      |  1 
crates/editor/src/editor.rs                       | 44 ++++++----------
crates/editor/src/element.rs                      |  1 
6 files changed, 36 insertions(+), 30 deletions(-)

Detailed changes

assets/keymaps/default-linux.json πŸ”—

@@ -501,6 +501,12 @@
       "tab": "editor::ConfirmCompletion"
     }
   },
+  {
+    "context": "Editor && inline_completion && !showing_completions",
+    "bindings": {
+      "tab": "editor::AcceptInlineCompletion"
+    }
+  },
   {
     "context": "Editor && showing_code_actions",
     "bindings": {

assets/keymaps/default-macos.json πŸ”—

@@ -515,6 +515,12 @@
       "tab": "editor::ConfirmCompletion"
     }
   },
+  {
+    "context": "Editor && inline_completion && !showing_completions",
+    "bindings": {
+      "tab": "editor::AcceptInlineCompletion"
+    }
+  },
   {
     "context": "Editor && showing_code_actions",
     "bindings": {

crates/copilot/src/copilot_completion_provider.rs πŸ”—

@@ -492,8 +492,8 @@ mod tests {
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
 
-            // Tabbing when there is an active suggestion inserts it.
-            editor.tab(&Default::default(), cx);
+            // AcceptInlineCompletion when there is an active suggestion inserts it.
+            editor.accept_inline_completion(&Default::default(), cx);
             assert!(!editor.has_active_inline_completion(cx));
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
@@ -550,8 +550,8 @@ mod tests {
             assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
             assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
 
-            // Tabbing again accepts the suggestion.
-            editor.tab(&Default::default(), cx);
+            // Using AcceptInlineCompletion again accepts the suggestion.
+            editor.accept_inline_completion(&Default::default(), cx);
             assert!(!editor.has_active_inline_completion(cx));
             assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
             assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");

crates/editor/src/actions.rs πŸ”—

@@ -143,6 +143,7 @@ gpui::actions!(
     editor,
     [
         AcceptPartialCopilotSuggestion,
+        AcceptInlineCompletion,
         AcceptPartialInlineCompletion,
         AddSelectionAbove,
         AddSelectionBelow,

crates/editor/src/editor.rs πŸ”—

@@ -4450,23 +4450,25 @@ impl Editor {
         }
     }
 
-    fn accept_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> bool {
-        if let Some(completion) = self.take_active_inline_completion(cx) {
-            if let Some(provider) = self.inline_completion_provider() {
-                provider.accept(cx);
-            }
-
-            cx.emit(EditorEvent::InputHandled {
-                utf16_range_to_replace: None,
-                text: completion.text.to_string().into(),
-            });
-            self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
-            self.refresh_inline_completion(true, cx);
-            cx.notify();
-            true
-        } else {
-            false
+    pub fn accept_inline_completion(
+        &mut self,
+        _: &AcceptInlineCompletion,
+        cx: &mut ViewContext<Self>,
+    ) {
+        let Some(completion) = self.take_active_inline_completion(cx) else {
+            return;
+        };
+        if let Some(provider) = self.inline_completion_provider() {
+            provider.accept(cx);
         }
+
+        cx.emit(EditorEvent::InputHandled {
+            utf16_range_to_replace: None,
+            text: completion.text.to_string().into(),
+        });
+        self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
+        self.refresh_inline_completion(true, cx);
+        cx.notify();
     }
 
     pub fn accept_partial_inline_completion(
@@ -4966,16 +4968,6 @@ impl Editor {
                 }
             }
 
-            // Accept copilot completion if there is only one selection and the cursor is not
-            // in the leading whitespace.
-            if self.selections.count() == 1
-                && cursor.column >= current_indent.len
-                && self.has_active_inline_completion(cx)
-            {
-                self.accept_inline_completion(cx);
-                return;
-            }
-
             // Otherwise, insert a hard or soft tab.
             let settings = buffer.settings_at(cursor, cx);
             let tab_size = if settings.hard_tabs {

crates/editor/src/element.rs πŸ”—

@@ -383,6 +383,7 @@ impl EditorElement {
         register_action(view, cx, Editor::unique_lines_case_insensitive);
         register_action(view, cx, Editor::unique_lines_case_sensitive);
         register_action(view, cx, Editor::accept_partial_inline_completion);
+        register_action(view, cx, Editor::accept_inline_completion);
         register_action(view, cx, Editor::revert_selected_hunks);
         register_action(view, cx, Editor::open_active_item_in_terminal)
     }