inline completions: Add action to toggle inline completions (#16947)

Thorsten Ball and Antonio created

This adds a new action: `editor: toggle inline completions`.

It allows users to toggle inline completions on/off for the current
buffer.

That toggling is not persistent and when the editor is closed, it's
gone.

That makes it easy to disable inline completions for a single text
buffer, for example, even if you want them on for other buffers.

When toggling on/off, the toggling also overwrites any language
settings. So if you have inline completions disabled for Go buffers,
toggling them on takes precedence over those settings.


Release Notes:

- Added a new editor action to allow toggling inline completions
(Copilot, Supermaven) on and off for the current buffer, taking
precedence over any settings.

Co-authored-by: Antonio <antonio@zed.dev>

Change summary

crates/assistant/src/inline_assistant.rs   |  2 
crates/assistant/src/prompt_library.rs     |  4 
crates/assistant/src/workflow/step_view.rs |  2 
crates/editor/src/actions.rs               |  1 
crates/editor/src/editor.rs                | 56 ++++++++++++++++++++---
crates/editor/src/element.rs               |  1 
crates/editor/src/hunk_diff.rs             |  2 
crates/feedback/src/feedback_modal.rs      |  2 
crates/language_tools/src/lsp_log.rs       |  2 
9 files changed, 56 insertions(+), 16 deletions(-)

Detailed changes

crates/assistant/src/inline_assistant.rs 🔗

@@ -1136,7 +1136,7 @@ impl InlineAssistant {
                     editor.set_show_gutter(false, cx);
                     editor.scroll_manager.set_forbid_vertical_scroll(true);
                     editor.set_read_only(true);
-                    editor.set_show_inline_completions(false);
+                    editor.set_show_inline_completions(Some(false), cx);
                     editor.highlight_rows::<DeletedLines>(
                         Anchor::min()..=Anchor::max(),
                         Some(cx.theme().status().deleted_background),

crates/assistant/src/prompt_library.rs 🔗

@@ -495,7 +495,7 @@ impl PromptLibrary {
                             editor.set_text(prompt_metadata.title.unwrap_or_default(), cx);
                             if prompt_id.is_built_in() {
                                 editor.set_read_only(true);
-                                editor.set_show_inline_completions(false);
+                                editor.set_show_inline_completions(Some(false), cx);
                             }
                             editor
                         });
@@ -510,7 +510,7 @@ impl PromptLibrary {
                             let mut editor = Editor::for_buffer(buffer, None, cx);
                             if prompt_id.is_built_in() {
                                 editor.set_read_only(true);
-                                editor.set_show_inline_completions(false);
+                                editor.set_show_inline_completions(Some(false), cx);
                             }
                             editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
                             editor.set_show_gutter(false, cx);

crates/assistant/src/workflow/step_view.rs 🔗

@@ -78,7 +78,7 @@ impl WorkflowStepView {
             editor.set_show_wrap_guides(false, cx);
             editor.set_show_indent_guides(false, cx);
             editor.set_read_only(true);
-            editor.set_show_inline_completions(false);
+            editor.set_show_inline_completions(Some(false), cx);
             editor.insert_blocks(
                 [
                     BlockProperties {

crates/editor/src/actions.rs 🔗

@@ -317,6 +317,7 @@ gpui::actions!(
         ToggleSelectionMenu,
         ToggleHunkDiff,
         ToggleInlayHints,
+        ToggleInlineCompletions,
         ToggleLineNumbers,
         ToggleRelativeLineNumbers,
         ToggleIndentGuides,

crates/editor/src/editor.rs 🔗

@@ -556,7 +556,7 @@ pub struct Editor {
     hovered_link_state: Option<HoveredLinkState>,
     inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
     active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
-    show_inline_completions: bool,
+    show_inline_completions_override: Option<bool>,
     inlay_hint_cache: InlayHintCache,
     expanded_hunks: ExpandedHunks,
     next_inlay_id: usize,
@@ -1912,7 +1912,7 @@ impl Editor {
             hovered_cursors: Default::default(),
             next_editor_action_id: EditorActionId::default(),
             editor_actions: Rc::default(),
-            show_inline_completions: mode == EditorMode::Full,
+            show_inline_completions_override: None,
             custom_context_menu: None,
             show_git_blame_gutter: false,
             show_git_blame_inline: false,
@@ -2305,8 +2305,49 @@ impl Editor {
         self.auto_replace_emoji_shortcode = auto_replace;
     }
 
-    pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
-        self.show_inline_completions = show_inline_completions;
+    pub fn toggle_inline_completions(
+        &mut self,
+        _: &ToggleInlineCompletions,
+        cx: &mut ViewContext<Self>,
+    ) {
+        if self.show_inline_completions_override.is_some() {
+            self.set_show_inline_completions(None, cx);
+        } else {
+            let cursor = self.selections.newest_anchor().head();
+            if let Some((buffer, cursor_buffer_position)) =
+                self.buffer.read(cx).text_anchor_for_position(cursor, cx)
+            {
+                let show_inline_completions =
+                    !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
+                self.set_show_inline_completions(Some(show_inline_completions), cx);
+            }
+        }
+    }
+
+    pub fn set_show_inline_completions(
+        &mut self,
+        show_inline_completions: Option<bool>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.show_inline_completions_override = show_inline_completions;
+        self.refresh_inline_completion(false, true, cx);
+    }
+
+    fn should_show_inline_completions(
+        &self,
+        buffer: &Model<Buffer>,
+        buffer_position: language::Anchor,
+        cx: &AppContext,
+    ) -> bool {
+        if let Some(provider) = self.inline_completion_provider() {
+            if let Some(show_inline_completions) = self.show_inline_completions_override {
+                show_inline_completions
+            } else {
+                self.mode == EditorMode::Full && provider.is_enabled(&buffer, buffer_position, cx)
+            }
+        } else {
+            false
+        }
     }
 
     pub fn set_use_modal_editing(&mut self, to: bool) {
@@ -4934,8 +4975,7 @@ impl Editor {
         let (buffer, cursor_buffer_position) =
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
         if !user_requested
-            && (!self.show_inline_completions
-                || !provider.is_enabled(&buffer, cursor_buffer_position, cx))
+            && !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
         {
             self.discard_inline_completion(false, cx);
             return None;
@@ -4955,9 +4995,7 @@ impl Editor {
         let cursor = self.selections.newest_anchor().head();
         let (buffer, cursor_buffer_position) =
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
-        if !self.show_inline_completions
-            || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
-        {
+        if !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx) {
             return None;
         }
 

crates/editor/src/element.rs 🔗

@@ -347,6 +347,7 @@ impl EditorElement {
         register_action(view, cx, Editor::toggle_relative_line_numbers);
         register_action(view, cx, Editor::toggle_indent_guides);
         register_action(view, cx, Editor::toggle_inlay_hints);
+        register_action(view, cx, Editor::toggle_inline_completions);
         register_action(view, cx, hover_popover::hover);
         register_action(view, cx, Editor::reveal_in_finder);
         register_action(view, cx, Editor::copy_path);

crates/editor/src/hunk_diff.rs 🔗

@@ -782,7 +782,7 @@ fn editor_with_deleted_text(
         editor.set_show_gutter(false, cx);
         editor.scroll_manager.set_forbid_vertical_scroll(true);
         editor.set_read_only(true);
-        editor.set_show_inline_completions(false);
+        editor.set_show_inline_completions(Some(false), cx);
         editor.highlight_rows::<DiffRowHighlight>(
             Anchor::min()..=Anchor::max(),
             Some(deleted_color),

crates/feedback/src/feedback_modal.rs 🔗

@@ -186,7 +186,7 @@ impl FeedbackModal {
             );
             editor.set_show_gutter(false, cx);
             editor.set_show_indent_guides(false, cx);
-            editor.set_show_inline_completions(false);
+            editor.set_show_inline_completions(Some(false), cx);
             editor.set_vertical_scroll_margin(5, cx);
             editor.set_use_modal_editing(false);
             editor

crates/language_tools/src/lsp_log.rs 🔗

@@ -647,7 +647,7 @@ impl LspLogView {
             editor.set_text(log_contents, cx);
             editor.move_to_end(&MoveToEnd, cx);
             editor.set_read_only(true);
-            editor.set_show_inline_completions(false);
+            editor.set_show_inline_completions(Some(false), cx);
             editor
         });
         let editor_subscription = cx.subscribe(