Revert "edit prediction: Improve UX around `disabled_globs` and `show_inline_completions` (#24207)"

Marshall Bowers created

This reverts commit 68b4f9ee1d8b8800b5adb885844d6111e387bc3b.

Change summary

Cargo.lock                                                      |   1 
assets/icons/zed_predict_disabled.svg                           |   6 
crates/copilot/src/copilot_completion_provider.rs               |  21 
crates/editor/src/editor.rs                                     | 143 
crates/inline_completion_button/Cargo.toml                      |   5 
crates/inline_completion_button/src/inline_completion_button.rs | 250 
crates/language/src/language_settings.rs                        |  13 
crates/supermaven/src/supermaven_completion_provider.rs         |  14 
crates/ui/src/components/context_menu.rs                        | 491 +-
crates/ui/src/components/icon.rs                                |   1 
crates/vim/src/vim.rs                                           |   2 
crates/zed/src/zed/quick_action_bar.rs                          |  35 
crates/zeta/src/zeta.rs                                         |  15 
13 files changed, 419 insertions(+), 578 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -6385,7 +6385,6 @@ dependencies = [
  "lsp",
  "paths",
  "project",
- "regex",
  "serde_json",
  "settings",
  "supermaven",

assets/icons/zed_predict_disabled.svg 🔗

@@ -1,6 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path opacity="0.6" fill-rule="evenodd" clip-rule="evenodd" d="M6.75 9.31247L8.25 10.5576V11.75H1.75V10.0803L4.49751 7.44273L5.65909 8.40693L3.73923 10.25H6.75V9.31247ZM8.25 5.85739V4.25H6.31358L8.25 5.85739ZM1.75 5.16209V7.1H3.25V6.4072L1.75 5.16209Z" fill="black"/>
-<path opacity="0.6" fill-rule="evenodd" clip-rule="evenodd" d="M10.9624 9.40853L11.9014 8L10.6241 6.08397L9.37598 6.91603L10.0986 8L9.80184 8.44518L10.9624 9.40853Z" fill="black"/>
-<path opacity="0.6" fill-rule="evenodd" clip-rule="evenodd" d="M12.8936 11.0116L14.9014 8L12.6241 4.58397L11.376 5.41603L13.0986 8L11.7331 10.0483L12.8936 11.0116Z" fill="black"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1225 13.809C14.0341 13.9146 13.877 13.9289 13.7711 13.8409L1.19311 3.40021C1.08659 3.31178 1.07221 3.15362 1.16104 3.04743L1.87752 2.19101C1.96588 2.0854 2.123 2.07112 2.22895 2.15906L14.8069 12.5998C14.9134 12.6882 14.9278 12.8464 14.839 12.9526L14.1225 13.809Z" fill="black"/>
-</svg>

crates/copilot/src/copilot_completion_provider.rs 🔗

@@ -2,7 +2,10 @@ use crate::{Completion, Copilot};
 use anyhow::Result;
 use gpui::{App, Context, Entity, EntityId, Task};
 use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
-use language::{language_settings::AllLanguageSettings, Buffer, OffsetRangeExt, ToOffset};
+use language::{
+    language_settings::{all_language_settings, AllLanguageSettings},
+    Buffer, OffsetRangeExt, ToOffset,
+};
 use settings::Settings;
 use std::{path::Path, time::Duration};
 
@@ -70,11 +73,19 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
 
     fn is_enabled(
         &self,
-        _buffer: &Entity<Buffer>,
-        _cursor_position: language::Anchor,
+        buffer: &Entity<Buffer>,
+        cursor_position: language::Anchor,
         cx: &App,
     ) -> bool {
-        self.copilot.read(cx).status().is_authorized()
+        if !self.copilot.read(cx).status().is_authorized() {
+            return false;
+        }
+
+        let buffer = buffer.read(cx);
+        let file = buffer.file();
+        let language = buffer.language_at(cursor_position);
+        let settings = all_language_settings(file, cx);
+        settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
     }
 
     fn refresh(
@@ -194,7 +205,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
     fn discard(&mut self, cx: &mut Context<Self>) {
         let settings = AllLanguageSettings::get_global(cx);
 
-        let copilot_enabled = settings.show_inline_completions(None, cx);
+        let copilot_enabled = settings.inline_completions_enabled(None, None, cx);
 
         if !copilot_enabled {
             return;

crates/editor/src/editor.rs 🔗

@@ -679,7 +679,7 @@ pub struct Editor {
     stale_inline_completion_in_menu: Option<InlineCompletionState>,
     // enable_inline_completions is a switch that Vim can use to disable
     // edit predictions based on its mode.
-    show_inline_completions: bool,
+    enable_inline_completions: bool,
     show_inline_completions_override: Option<bool>,
     menu_inline_completions_policy: MenuInlineCompletionsPolicy,
     inlay_hint_cache: InlayHintCache,
@@ -1387,7 +1387,7 @@ impl Editor {
             next_editor_action_id: EditorActionId::default(),
             editor_actions: Rc::default(),
             show_inline_completions_override: None,
-            show_inline_completions: true,
+            enable_inline_completions: true,
             menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
             custom_context_menu: None,
             show_git_blame_gutter: false,
@@ -1817,9 +1817,9 @@ impl Editor {
         self.input_enabled = input_enabled;
     }
 
-    pub fn set_show_inline_completions_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
-        self.show_inline_completions = enabled;
-        if !self.show_inline_completions {
+    pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
+        self.enable_inline_completions = enabled;
+        if !self.enable_inline_completions {
             self.take_active_inline_completion(cx);
             cx.notify();
         }
@@ -1870,11 +1870,8 @@ impl Editor {
             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_in_buffer(
-                    &buffer,
-                    cursor_buffer_position,
-                    cx,
-                );
+                let show_inline_completions =
+                    !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
                 self.set_show_inline_completions(Some(show_inline_completions), window, cx);
             }
         }
@@ -1890,6 +1887,42 @@ impl Editor {
         self.refresh_inline_completion(false, true, window, cx);
     }
 
+    pub fn inline_completions_enabled(&self, cx: &App) -> bool {
+        let cursor = self.selections.newest_anchor().head();
+        if let Some((buffer, buffer_position)) =
+            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
+        {
+            self.should_show_inline_completions(&buffer, buffer_position, cx)
+        } else {
+            false
+        }
+    }
+
+    fn should_show_inline_completions(
+        &self,
+        buffer: &Entity<Buffer>,
+        buffer_position: language::Anchor,
+        cx: &App,
+    ) -> bool {
+        if !self.snippet_stack.is_empty() {
+            return false;
+        }
+
+        if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
+            return false;
+        }
+
+        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
+        }
+    }
+
     fn inline_completions_disabled_in_scope(
         &self,
         buffer: &Entity<Buffer>,
@@ -4616,18 +4649,9 @@ impl Editor {
         let (buffer, cursor_buffer_position) =
             self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 
-        if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
-            self.discard_inline_completion(false, cx);
-            return None;
-        }
-
         if !user_requested
-            && (!self.show_inline_completions
-                || !self.should_show_inline_completions_in_buffer(
-                    &buffer,
-                    cursor_buffer_position,
-                    cx,
-                )
+            && (!self.enable_inline_completions
+                || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
                 || !self.is_focused(window)
                 || buffer.read(cx).is_empty())
         {
@@ -4640,77 +4664,6 @@ impl Editor {
         Some(())
     }
 
-    pub fn should_show_inline_completions(&self, cx: &App) -> bool {
-        let cursor = self.selections.newest_anchor().head();
-        if let Some((buffer, cursor_position)) =
-            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
-        {
-            self.should_show_inline_completions_in_buffer(&buffer, cursor_position, cx)
-        } else {
-            false
-        }
-    }
-
-    fn should_show_inline_completions_in_buffer(
-        &self,
-        buffer: &Entity<Buffer>,
-        buffer_position: language::Anchor,
-        cx: &App,
-    ) -> bool {
-        if !self.snippet_stack.is_empty() {
-            return false;
-        }
-
-        if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
-            return false;
-        }
-
-        if let Some(show_inline_completions) = self.show_inline_completions_override {
-            show_inline_completions
-        } else {
-            let buffer = buffer.read(cx);
-            self.mode == EditorMode::Full
-                && language_settings(
-                    buffer.language_at(buffer_position).map(|l| l.name()),
-                    buffer.file(),
-                    cx,
-                )
-                .show_inline_completions
-        }
-    }
-
-    pub fn inline_completions_enabled(&self, cx: &App) -> bool {
-        let cursor = self.selections.newest_anchor().head();
-        if let Some((buffer, cursor_position)) =
-            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
-        {
-            self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
-        } else {
-            false
-        }
-    }
-
-    fn inline_completions_enabled_in_buffer(
-        &self,
-        buffer: &Entity<Buffer>,
-        buffer_position: language::Anchor,
-        cx: &App,
-    ) -> bool {
-        maybe!({
-            let provider = self.inline_completion_provider()?;
-            if !provider.is_enabled(&buffer, buffer_position, cx) {
-                return Some(false);
-            }
-            let buffer = buffer.read(cx);
-            let Some(file) = buffer.file() else {
-                return Some(true);
-            };
-            let settings = all_language_settings(Some(file), cx);
-            Some(settings.inline_completions_enabled_for_path(file.path()))
-        })
-        .unwrap_or(false)
-    }
-
     fn cycle_inline_completion(
         &mut self,
         direction: Direction,
@@ -4721,8 +4674,8 @@ 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
-            || !self.should_show_inline_completions_in_buffer(&buffer, cursor_buffer_position, cx)
+        if !self.enable_inline_completions
+            || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
         {
             return None;
         }
@@ -5060,7 +5013,7 @@ impl Editor {
                 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
         if completions_menu_has_precedence
             || !offset_selection.is_empty()
-            || !self.show_inline_completions
+            || !self.enable_inline_completions
             || self
                 .active_inline_completion
                 .as_ref()

crates/inline_completion_button/Cargo.toml 🔗

@@ -14,7 +14,6 @@ doctest = false
 
 [dependencies]
 anyhow.workspace = true
-client.workspace = true
 copilot.workspace = true
 editor.workspace = true
 feature_flags.workspace = true
@@ -23,14 +22,14 @@ gpui.workspace = true
 inline_completion.workspace = true
 language.workspace = true
 paths.workspace = true
-regex.workspace = true
 settings.workspace = true
 supermaven.workspace = true
-telemetry.workspace = true
 ui.workspace = true
 workspace.workspace = true
 zed_actions.workspace = true
 zeta.workspace = true
+client.workspace = true
+telemetry.workspace = true
 
 [dev-dependencies]
 copilot = { workspace = true, features = ["test-support"] }

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -17,12 +17,8 @@ use language::{
     },
     File, Language,
 };
-use regex::Regex;
 use settings::{update_settings_file, Settings, SettingsStore};
-use std::{
-    sync::{Arc, LazyLock},
-    time::Duration,
-};
+use std::{path::Path, sync::Arc, time::Duration};
 use supermaven::{AccountStatus, Supermaven};
 use ui::{
     prelude::*, Clickable, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, PopoverMenu,
@@ -75,7 +71,9 @@ impl Render for InlineCompletionButton {
                 };
                 let status = copilot.read(cx).status();
 
-                let enabled = self.editor_enabled.unwrap_or(false);
+                let enabled = self.editor_enabled.unwrap_or_else(|| {
+                    all_language_settings.inline_completions_enabled(None, None, cx)
+                });
 
                 let icon = match status {
                     Status::Error(_) => IconName::CopilotError,
@@ -230,35 +228,25 @@ impl Render for InlineCompletionButton {
                     return div();
                 }
 
-                let enabled = self.editor_enabled.unwrap_or(false);
-
-                let zeta_icon = if enabled {
-                    IconName::ZedPredict
-                } else {
-                    IconName::ZedPredictDisabled
-                };
+                fn icon_button() -> IconButton {
+                    IconButton::new("zed-predict-pending-button", IconName::ZedPredict)
+                        .shape(IconButtonShape::Square)
+                }
 
                 let current_user_terms_accepted =
                     self.user_store.read(cx).current_user_has_accepted_terms();
 
-                let icon_button = || {
-                    let base = IconButton::new("zed-predict-pending-button", zeta_icon)
-                        .shape(IconButtonShape::Square);
-
-                    match (
-                        current_user_terms_accepted,
-                        self.popover_menu_handle.is_deployed(),
-                        enabled,
-                    ) {
-                        (Some(false) | None, _, _) => {
-                            let signed_in = current_user_terms_accepted.is_some();
-                            let tooltip_meta = if signed_in {
-                                "Read Terms of Service"
-                            } else {
-                                "Sign in to use"
-                            };
+                if !current_user_terms_accepted.unwrap_or(false) {
+                    let signed_in = current_user_terms_accepted.is_some();
+                    let tooltip_meta = if signed_in {
+                        "Read Terms of Service"
+                    } else {
+                        "Sign in to use"
+                    };
 
-                            base.tooltip(move |window, cx| {
+                    return div().child(
+                        icon_button()
+                            .tooltip(move |window, cx| {
                                 Tooltip::with_meta(
                                     "Edit Predictions",
                                     None,
@@ -267,37 +255,27 @@ impl Render for InlineCompletionButton {
                                     cx,
                                 )
                             })
-                            .on_click(cx.listener(
-                                move |_, _, window, cx| {
-                                    telemetry::event!(
-                                        "Pending ToS Clicked",
-                                        source = "Edit Prediction Status Button"
-                                    );
-                                    window.dispatch_action(
-                                        zed_actions::OpenZedPredictOnboarding.boxed_clone(),
-                                        cx,
-                                    );
-                                },
-                            ))
-                        }
-                        (Some(true), true, _) => base,
-                        (Some(true), false, true) => base.tooltip(|window, cx| {
-                            Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx)
-                        }),
-                        (Some(true), false, false) => base.tooltip(|window, cx| {
-                            Tooltip::with_meta(
-                                "Edit Prediction",
-                                Some(&ToggleMenu),
-                                "Disabled For This File",
-                                window,
-                                cx,
-                            )
-                        }),
-                    }
-                };
+                            .on_click(cx.listener(move |_, _, window, cx| {
+                                telemetry::event!(
+                                    "Pending ToS Clicked",
+                                    source = "Edit Prediction Status Button"
+                                );
+                                window.dispatch_action(
+                                    zed_actions::OpenZedPredictOnboarding.boxed_clone(),
+                                    cx,
+                                );
+                            })),
+                    );
+                }
 
                 let this = cx.entity().clone();
 
+                if !self.popover_menu_handle.is_deployed() {
+                    icon_button().tooltip(|window, cx| {
+                        Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx)
+                    });
+                }
+
                 let mut popover_menu = PopoverMenu::new("zeta")
                     .menu(move |window, cx| {
                         Some(this.update(cx, |this, cx| this.build_zeta_context_menu(window, cx)))
@@ -384,10 +362,15 @@ impl InlineCompletionButton {
         })
     }
 
+    // Predict Edits at Cursor – alt-tab
+    // Automatically Predict:
+    // ✓ PATH
+    // ✓ Rust
+    // ✓ All Files
     pub fn build_language_settings_menu(&self, mut menu: ContextMenu, cx: &mut App) -> ContextMenu {
         let fs = self.fs.clone();
 
-        menu = menu.header("Show Predict Edits For");
+        menu = menu.header("Predict Edits For:");
 
         if let Some(language) = self.language.clone() {
             let fs = fs.clone();
@@ -398,39 +381,66 @@ impl InlineCompletionButton {
             menu = menu.toggleable_entry(
                 language.name(),
                 language_enabled,
-                IconPosition::End,
+                IconPosition::Start,
                 None,
                 move |_, cx| {
-                    toggle_show_inline_completions_for_language(language.clone(), fs.clone(), cx)
+                    toggle_inline_completions_for_language(language.clone(), fs.clone(), cx)
                 },
             );
         }
 
         let settings = AllLanguageSettings::get_global(cx);
-        let globally_enabled = settings.show_inline_completions(None, cx);
+        if let Some(file) = &self.file {
+            let path = file.path().clone();
+            let path_enabled = settings.inline_completions_enabled_for_path(&path);
+
+            menu = menu.toggleable_entry(
+                "This File",
+                path_enabled,
+                IconPosition::Start,
+                None,
+                move |window, cx| {
+                    if let Some(workspace) = window.root().flatten() {
+                        let workspace = workspace.downgrade();
+                        window
+                            .spawn(cx, |cx| {
+                                configure_disabled_globs(
+                                    workspace,
+                                    path_enabled.then_some(path.clone()),
+                                    cx,
+                                )
+                            })
+                            .detach_and_log_err(cx);
+                    }
+                },
+            );
+        }
+
+        let globally_enabled = settings.inline_completions_enabled(None, None, cx);
         menu = menu.toggleable_entry(
             "All Files",
             globally_enabled,
-            IconPosition::End,
+            IconPosition::Start,
             None,
             move |_, cx| toggle_inline_completions_globally(fs.clone(), cx),
         );
-        menu = menu.separator().header("Privacy Settings");
 
         if let Some(provider) = &self.inline_completion_provider {
             let data_collection = provider.data_collection_state(cx);
+
             if data_collection.is_supported() {
                 let provider = provider.clone();
                 let enabled = data_collection.is_enabled();
 
+                menu = menu
+                    .separator()
+                    .header("Help Improve The Model")
+                    .header("Valid Only For OSS Projects");
                 menu = menu.item(
                     // TODO: We want to add something later that communicates whether
                     // the current project is open-source.
                     ContextMenuEntry::new("Share Training Data")
-                        .toggleable(IconPosition::End, data_collection.is_enabled())
-                        .documentation_aside(|_| {
-                            Label::new("Zed automatically detects if your project is open-source. This setting is only applicable in such cases.").into_any_element()
-                        })
+                        .toggleable(IconPosition::Start, enabled)
                         .handler(move |_, cx| {
                             provider.toggle_data_collection(cx);
 
@@ -445,42 +455,11 @@ impl InlineCompletionButton {
                                     source = "Edit Prediction Status Menu"
                                 );
                             }
-                        })
-                )
+                        }),
+                );
             }
         }
 
-        menu = menu.item(
-            ContextMenuEntry::new("Exclude Files")
-                .documentation_aside(|_| {
-                    Label::new("This item takes you to the settings where you can specify files that will never be captured by any edit prediction model. You can list both specific file extensions and individual file names.").into_any_element()
-                })
-                .handler(move |window, cx| {
-                    if let Some(workspace) = window.root().flatten() {
-                        let workspace = workspace.downgrade();
-                        window
-                            .spawn(cx, |cx| {
-                                open_disabled_globs_setting_in_editor(
-                                    workspace,
-                                    cx,
-                                )
-                            })
-                            .detach_and_log_err(cx);
-                    }
-                }),
-        );
-
-        if self.file.as_ref().map_or(false, |file| {
-            !all_language_settings(Some(file), cx).inline_completions_enabled_for_path(file.path())
-        }) {
-            menu = menu.item(
-                ContextMenuEntry::new("This file is excluded.")
-                    .disabled(true)
-                    .icon(IconName::ZedPredictDisabled)
-                    .icon_size(IconSize::Small),
-            );
-        }
-
         if let Some(editor_focus_handle) = self.editor_focus_handle.clone() {
             menu = menu
                 .separator()
@@ -567,11 +546,12 @@ impl InlineCompletionButton {
         self.editor_enabled = {
             let file = file.as_ref();
             Some(
-                file.map(|file| {
-                    all_language_settings(Some(file), cx)
-                        .inline_completions_enabled_for_path(file.path())
-                })
-                .unwrap_or(true),
+                file.map(|file| !file.is_private()).unwrap_or(true)
+                    && all_language_settings(file, cx).inline_completions_enabled(
+                        language,
+                        file.map(|file| file.path().as_ref()),
+                        cx,
+                    ),
             )
         };
         self.inline_completion_provider = editor.inline_completion_provider();
@@ -636,8 +616,9 @@ impl SupermavenButtonStatus {
     }
 }
 
-async fn open_disabled_globs_setting_in_editor(
+async fn configure_disabled_globs(
     workspace: WeakEntity<Workspace>,
+    path_to_disable: Option<Arc<Path>>,
     mut cx: AsyncWindowContext,
 ) -> Result<()> {
     let settings_editor = workspace
@@ -656,34 +637,34 @@ async fn open_disabled_globs_setting_in_editor(
             let text = item.buffer().read(cx).snapshot(cx).text();
 
             let settings = cx.global::<SettingsStore>();
-
-            // Ensure that we always have "inline_completions { "disabled_globs": [] }"
             let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
-                file.inline_completions
-                    .get_or_insert_with(Default::default)
-                    .disabled_globs
-                    .get_or_insert_with(Vec::new);
+                let copilot = file.inline_completions.get_or_insert_with(Default::default);
+                let globs = copilot.disabled_globs.get_or_insert_with(|| {
+                    settings
+                        .get::<AllLanguageSettings>(None)
+                        .inline_completions
+                        .disabled_globs
+                        .iter()
+                        .map(|glob| glob.glob().to_string())
+                        .collect()
+                });
+
+                if let Some(path_to_disable) = &path_to_disable {
+                    globs.push(path_to_disable.to_string_lossy().into_owned());
+                } else {
+                    globs.clear();
+                }
             });
 
             if !edits.is_empty() {
-                item.edit(edits.iter().cloned(), cx);
-            }
-
-            let text = item.buffer().read(cx).snapshot(cx).text();
-
-            static DISABLED_GLOBS_REGEX: LazyLock<Regex> = LazyLock::new(|| {
-                Regex::new(r#""disabled_globs":\s*\[\s*(?P<content>(?:.|\n)*?)\s*\]"#).unwrap()
-            });
-            // Only capture [...]
-            let range = DISABLED_GLOBS_REGEX.captures(&text).and_then(|captures| {
-                captures
-                    .name("content")
-                    .map(|inner_match| inner_match.start()..inner_match.end())
-            });
-            if let Some(range) = range {
                 item.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
-                    selections.select_ranges(vec![range]);
+                    selections.select_ranges(edits.iter().map(|e| e.0.clone()));
                 });
+
+                // When *enabling* a path, don't actually perform an edit, just select the range.
+                if path_to_disable.is_some() {
+                    item.edit(edits.iter().cloned(), cx);
+                }
             }
         })?;
 
@@ -691,7 +672,8 @@ async fn open_disabled_globs_setting_in_editor(
 }
 
 fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut App) {
-    let show_inline_completions = all_language_settings(None, cx).show_inline_completions(None, cx);
+    let show_inline_completions =
+        all_language_settings(None, cx).inline_completions_enabled(None, None, cx);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
         file.defaults.show_inline_completions = Some(!show_inline_completions)
     });
@@ -705,13 +687,9 @@ fn set_completion_provider(fs: Arc<dyn Fs>, cx: &mut App, provider: InlineComple
     });
 }
 
-fn toggle_show_inline_completions_for_language(
-    language: Arc<Language>,
-    fs: Arc<dyn Fs>,
-    cx: &mut App,
-) {
+fn toggle_inline_completions_for_language(language: Arc<Language>, fs: Arc<dyn Fs>, cx: &mut App) {
     let show_inline_completions =
-        all_language_settings(None, cx).show_inline_completions(Some(&language), cx);
+        all_language_settings(None, cx).inline_completions_enabled(Some(&language), None, cx);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
         file.languages
             .entry(language.name())

crates/language/src/language_settings.rs 🔗

@@ -886,7 +886,18 @@ impl AllLanguageSettings {
     }
 
     /// Returns whether edit predictions are enabled for the given language and path.
-    pub fn show_inline_completions(&self, language: Option<&Arc<Language>>, cx: &App) -> bool {
+    pub fn inline_completions_enabled(
+        &self,
+        language: Option<&Arc<Language>>,
+        path: Option<&Path>,
+        cx: &App,
+    ) -> bool {
+        if let Some(path) = path {
+            if !self.inline_completions_enabled_for_path(path) {
+                return false;
+            }
+        }
+
         self.language(None, language.map(|l| l.name()).as_ref(), cx)
             .show_inline_completions
     }

crates/supermaven/src/supermaven_completion_provider.rs 🔗

@@ -3,7 +3,7 @@ use anyhow::Result;
 use futures::StreamExt as _;
 use gpui::{App, Context, Entity, EntityId, Task};
 use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
-use language::{Anchor, Buffer, BufferSnapshot};
+use language::{language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot};
 use std::{
     ops::{AddAssign, Range},
     path::Path,
@@ -113,8 +113,16 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
         false
     }
 
-    fn is_enabled(&self, _buffer: &Entity<Buffer>, _cursor_position: Anchor, cx: &App) -> bool {
-        self.supermaven.read(cx).is_enabled()
+    fn is_enabled(&self, buffer: &Entity<Buffer>, cursor_position: Anchor, cx: &App) -> bool {
+        if !self.supermaven.read(cx).is_enabled() {
+            return false;
+        }
+
+        let buffer = buffer.read(cx);
+        let file = buffer.file();
+        let language = buffer.language_at(cursor_position);
+        let settings = all_language_settings(file, cx);
+        settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
     }
 
     fn is_refreshing(&self) -> bool {

crates/ui/src/components/context_menu.rs 🔗

@@ -47,7 +47,6 @@ pub struct ContextMenuEntry {
     handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
     action: Option<Box<dyn Action>>,
     disabled: bool,
-    documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>,
 }
 
 impl ContextMenuEntry {
@@ -62,7 +61,6 @@ impl ContextMenuEntry {
             handler: Rc::new(|_, _, _| {}),
             action: None,
             disabled: false,
-            documentation_aside: None,
         }
     }
 
@@ -110,14 +108,6 @@ impl ContextMenuEntry {
         self.disabled = disabled;
         self
     }
-
-    pub fn documentation_aside(
-        mut self,
-        element: impl Fn(&mut App) -> AnyElement + 'static,
-    ) -> Self {
-        self.documentation_aside = Some(Rc::new(element));
-        self
-    }
 }
 
 impl From<ContextMenuEntry> for ContextMenuItem {
@@ -135,7 +125,6 @@ pub struct ContextMenu {
     clicked: bool,
     _on_blur_subscription: Subscription,
     keep_open_on_confirm: bool,
-    documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>,
 }
 
 impl Focusable for ContextMenu {
@@ -172,7 +161,6 @@ impl ContextMenu {
                     clicked: false,
                     _on_blur_subscription,
                     keep_open_on_confirm: false,
-                    documentation_aside: None,
                 },
                 window,
                 cx,
@@ -221,7 +209,6 @@ impl ContextMenu {
             icon_color: None,
             action,
             disabled: false,
-            documentation_aside: None,
         }));
         self
     }
@@ -244,7 +231,6 @@ impl ContextMenu {
             icon_color: None,
             action,
             disabled: false,
-            documentation_aside: None,
         }));
         self
     }
@@ -295,7 +281,6 @@ impl ContextMenu {
             icon_size: IconSize::Small,
             icon_color: None,
             disabled: false,
-            documentation_aside: None,
         }));
         self
     }
@@ -309,6 +294,7 @@ impl ContextMenu {
             toggle: None,
             label: label.into(),
             action: Some(action.boxed_clone()),
+
             handler: Rc::new(move |context, window, cx| {
                 if let Some(context) = &context {
                     window.focus(context);
@@ -320,7 +306,6 @@ impl ContextMenu {
             icon_position: IconPosition::End,
             icon_color: None,
             disabled: true,
-            documentation_aside: None,
         }));
         self
     }
@@ -329,6 +314,7 @@ impl ContextMenu {
         self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
             toggle: None,
             label: label.into(),
+
             action: Some(action.boxed_clone()),
             handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)),
             icon: Some(IconName::ArrowUpRight),
@@ -336,7 +322,6 @@ impl ContextMenu {
             icon_position: IconPosition::End,
             icon_color: None,
             disabled: false,
-            documentation_aside: None,
         }));
         self
     }
@@ -371,16 +356,15 @@ impl ContextMenu {
     }
 
     fn select_first(&mut self, _: &SelectFirst, _: &mut Window, cx: &mut Context<Self>) {
-        if let Some(ix) = self.items.iter().position(|item| item.is_selectable()) {
-            self.select_index(ix);
-        }
+        self.selected_index = self.items.iter().position(|item| item.is_selectable());
         cx.notify();
     }
 
     pub fn select_last(&mut self) -> Option<usize> {
         for (ix, item) in self.items.iter().enumerate().rev() {
             if item.is_selectable() {
-                return self.select_index(ix);
+                self.selected_index = Some(ix);
+                return Some(ix);
             }
         }
         None
@@ -400,7 +384,7 @@ impl ContextMenu {
             } else {
                 for (ix, item) in self.items.iter().enumerate().skip(next_index) {
                     if item.is_selectable() {
-                        self.select_index(ix);
+                        self.selected_index = Some(ix);
                         cx.notify();
                         break;
                     }
@@ -418,7 +402,7 @@ impl ContextMenu {
             } else {
                 for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
                     if item.is_selectable() {
-                        self.select_index(ix);
+                        self.selected_index = Some(ix);
                         cx.notify();
                         break;
                     }
@@ -429,20 +413,6 @@ impl ContextMenu {
         }
     }
 
-    fn select_index(&mut self, ix: usize) -> Option<usize> {
-        self.documentation_aside = None;
-        let item = self.items.get(ix)?;
-        if item.is_selectable() {
-            self.selected_index = Some(ix);
-            if let ContextMenuItem::Entry(entry) = item {
-                if let Some(callback) = &entry.documentation_aside {
-                    self.documentation_aside = Some((ix, callback.clone()));
-                }
-            }
-        }
-        Some(ix)
-    }
-
     pub fn on_action_dispatch(
         &mut self,
         dispatched: &dyn Action,
@@ -466,7 +436,7 @@ impl ContextMenu {
                 false
             }
         }) {
-            self.select_index(ix);
+            self.selected_index = Some(ix);
             self.delayed = true;
             cx.notify();
             let action = dispatched.boxed_clone();
@@ -509,275 +479,198 @@ impl Render for ContextMenu {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
 
-        let aside = self
-            .documentation_aside
-            .as_ref()
-            .map(|(_, callback)| callback.clone());
-
-        h_flex()
-            .w_full()
-            .items_start()
-            .gap_1()
-            .when_some(aside, |this, aside| {
-                this.child(
-                    WithRemSize::new(ui_font_size)
-                        .occlude()
-                        .elevation_2(cx)
-                        .p_2()
-                        .max_w_80()
-                        .child(aside(cx)),
-                )
-            })
+        WithRemSize::new(ui_font_size)
+            .occlude()
+            .elevation_2(cx)
+            .flex()
+            .flex_row()
             .child(
-                WithRemSize::new(ui_font_size)
-                    .occlude()
-                    .elevation_2(cx)
-                    .flex()
-                    .flex_row()
-                    .child(
-                        v_flex()
-                            .id("context-menu")
-                            .min_w(px(200.))
-                            .max_h(vh(0.75, window))
-                            .flex_1()
-                            .overflow_y_scroll()
-                            .track_focus(&self.focus_handle(cx))
-                            .on_mouse_down_out(cx.listener(|this, _, window, cx| {
-                                this.cancel(&menu::Cancel, window, cx)
-                            }))
-                            .key_context("menu")
-                            .on_action(cx.listener(ContextMenu::select_first))
-                            .on_action(cx.listener(ContextMenu::handle_select_last))
-                            .on_action(cx.listener(ContextMenu::select_next))
-                            .on_action(cx.listener(ContextMenu::select_prev))
-                            .on_action(cx.listener(ContextMenu::confirm))
-                            .on_action(cx.listener(ContextMenu::cancel))
-                            .when(!self.delayed, |mut el| {
-                                for item in self.items.iter() {
-                                    if let ContextMenuItem::Entry(ContextMenuEntry {
-                                        action: Some(action),
-                                        disabled: false,
-                                        ..
-                                    }) = item
-                                    {
-                                        el = el.on_boxed_action(
-                                            &**action,
-                                            cx.listener(ContextMenu::on_action_dispatch),
-                                        );
-                                    }
+                v_flex()
+                    .id("context-menu")
+                    .min_w(px(200.))
+                    .max_h(vh(0.75, window))
+                    .flex_1()
+                    .overflow_y_scroll()
+                    .track_focus(&self.focus_handle(cx))
+                    .on_mouse_down_out(
+                        cx.listener(|this, _, window, cx| this.cancel(&menu::Cancel, window, cx)),
+                    )
+                    .key_context("menu")
+                    .on_action(cx.listener(ContextMenu::select_first))
+                    .on_action(cx.listener(ContextMenu::handle_select_last))
+                    .on_action(cx.listener(ContextMenu::select_next))
+                    .on_action(cx.listener(ContextMenu::select_prev))
+                    .on_action(cx.listener(ContextMenu::confirm))
+                    .on_action(cx.listener(ContextMenu::cancel))
+                    .when(!self.delayed, |mut el| {
+                        for item in self.items.iter() {
+                            if let ContextMenuItem::Entry(ContextMenuEntry {
+                                action: Some(action),
+                                disabled: false,
+                                ..
+                            }) = item
+                            {
+                                el = el.on_boxed_action(
+                                    &**action,
+                                    cx.listener(ContextMenu::on_action_dispatch),
+                                );
+                            }
+                        }
+                        el
+                    })
+                    .child(List::new().children(self.items.iter_mut().enumerate().map(
+                        |(ix, item)| {
+                            match item {
+                                ContextMenuItem::Separator => ListSeparator.into_any_element(),
+                                ContextMenuItem::Header(header) => {
+                                    ListSubHeader::new(header.clone())
+                                        .inset(true)
+                                        .into_any_element()
                                 }
-                                el
-                            })
-                            .child(List::new().children(self.items.iter_mut().enumerate().map(
-                                |(ix, item)| {
-                                    match item {
-                                        ContextMenuItem::Separator => {
-                                            ListSeparator.into_any_element()
-                                        }
-                                        ContextMenuItem::Header(header) => {
-                                            ListSubHeader::new(header.clone())
-                                                .inset(true)
-                                                .into_any_element()
-                                        }
-                                        ContextMenuItem::Label(label) => ListItem::new(ix)
-                                            .inset(true)
-                                            .disabled(true)
-                                            .child(Label::new(label.clone()))
-                                            .into_any_element(),
-                                        ContextMenuItem::Entry(ContextMenuEntry {
-                                            toggle,
-                                            label,
-                                            handler,
-                                            icon,
-                                            icon_position,
-                                            icon_size,
-                                            icon_color,
-                                            action,
-                                            disabled,
-                                            documentation_aside,
-                                        }) => {
-                                            let handler = handler.clone();
-                                            let menu = cx.entity().downgrade();
-                                            let icon_color = if *disabled {
-                                                Color::Muted
-                                            } else {
-                                                icon_color.unwrap_or(Color::Default)
-                                            };
-                                            let label_color = if *disabled {
-                                                Color::Muted
+                                ContextMenuItem::Label(label) => ListItem::new(ix)
+                                    .inset(true)
+                                    .disabled(true)
+                                    .child(Label::new(label.clone()))
+                                    .into_any_element(),
+                                ContextMenuItem::Entry(ContextMenuEntry {
+                                    toggle,
+                                    label,
+                                    handler,
+                                    icon,
+                                    icon_position,
+                                    icon_size,
+                                    icon_color,
+                                    action,
+                                    disabled,
+                                }) => {
+                                    let handler = handler.clone();
+                                    let menu = cx.entity().downgrade();
+                                    let icon_color = if *disabled {
+                                        Color::Muted
+                                    } else {
+                                        icon_color.unwrap_or(Color::Default)
+                                    };
+                                    let label_color = if *disabled {
+                                        Color::Muted
+                                    } else {
+                                        Color::Default
+                                    };
+                                    let label_element = if let Some(icon_name) = icon {
+                                        h_flex()
+                                            .gap_1p5()
+                                            .when(*icon_position == IconPosition::Start, |flex| {
+                                                flex.child(
+                                                    Icon::new(*icon_name)
+                                                        .size(*icon_size)
+                                                        .color(icon_color),
+                                                )
+                                            })
+                                            .child(Label::new(label.clone()).color(label_color))
+                                            .when(*icon_position == IconPosition::End, |flex| {
+                                                flex.child(
+                                                    Icon::new(*icon_name)
+                                                        .size(*icon_size)
+                                                        .color(icon_color),
+                                                )
+                                            })
+                                            .into_any_element()
+                                    } else {
+                                        Label::new(label.clone())
+                                            .color(label_color)
+                                            .into_any_element()
+                                    };
+
+                                    ListItem::new(ix)
+                                        .inset(true)
+                                        .disabled(*disabled)
+                                        .toggle_state(Some(ix) == self.selected_index)
+                                        .when_some(*toggle, |list_item, (position, toggled)| {
+                                            let contents = if toggled {
+                                                v_flex().flex_none().child(
+                                                    Icon::new(IconName::Check).color(Color::Accent),
+                                                )
                                             } else {
-                                                Color::Default
+                                                v_flex()
+                                                    .flex_none()
+                                                    .size(IconSize::default().rems())
                                             };
-                                            let label_element = if let Some(icon_name) = icon {
-                                                h_flex()
-                                                    .gap_1p5()
-                                                    .when(
-                                                        *icon_position == IconPosition::Start,
-                                                        |flex| {
-                                                            flex.child(
-                                                                Icon::new(*icon_name)
-                                                                    .size(*icon_size)
-                                                                    .color(icon_color),
+                                            match position {
+                                                IconPosition::Start => {
+                                                    list_item.start_slot(contents)
+                                                }
+                                                IconPosition::End => list_item.end_slot(contents),
+                                            }
+                                        })
+                                        .child(
+                                            h_flex()
+                                                .w_full()
+                                                .justify_between()
+                                                .child(label_element)
+                                                .debug_selector(|| format!("MENU_ITEM-{}", label))
+                                                .children(action.as_ref().and_then(|action| {
+                                                    self.action_context
+                                                        .as_ref()
+                                                        .map(|focus| {
+                                                            KeyBinding::for_action_in(
+                                                                &**action, focus, window,
                                                             )
-                                                        },
-                                                    )
-                                                    .child(
-                                                        Label::new(label.clone())
-                                                            .color(label_color),
-                                                    )
-                                                    .when(
-                                                        *icon_position == IconPosition::End,
-                                                        |flex| {
-                                                            flex.child(
-                                                                Icon::new(*icon_name)
-                                                                    .size(*icon_size)
-                                                                    .color(icon_color),
+                                                        })
+                                                        .unwrap_or_else(|| {
+                                                            KeyBinding::for_action(
+                                                                &**action, window,
                                                             )
-                                                        },
-                                                    )
-                                                    .into_any_element()
-                                            } else {
-                                                Label::new(label.clone())
-                                                    .color(label_color)
-                                                    .into_any_element()
-                                            };
-                                            let documentation_aside_callback =
-                                                documentation_aside.clone();
-                                            div()
-                                                .id(("context-menu-child", ix))
-                                                .when_some(
-                                                    documentation_aside_callback,
-                                                    |this, documentation_aside_callback| {
-                                                        this.occlude().on_hover(cx.listener(
-                                                            move |menu, hovered, _, cx| {
-                                                                if *hovered {
-                                                                    menu.documentation_aside = Some((ix, documentation_aside_callback.clone()));
-                                                                    cx.notify();
-                                                                } else if matches!(menu.documentation_aside, Some((id, _)) if id == ix) {
-                                                                    menu.documentation_aside = None;
-                                                                    cx.notify();
-                                                                }
-                                                            },
-                                                        ))
-                                                    },
-                                                )
-                                                .child(
-                                                    ListItem::new(ix)
-                                                        .inset(true)
-                                                        .disabled(*disabled)
-                                                        .toggle_state(
-                                                            Some(ix) == self.selected_index,
-                                                        )
-                                                        .when_some(
-                                                            *toggle,
-                                                            |list_item, (position, toggled)| {
-                                                                let contents = if toggled {
-                                                                    v_flex().flex_none().child(
-                                                                        Icon::new(IconName::Check)
-                                                                            .color(Color::Accent),
-                                                                    )
-                                                                } else {
-                                                                    v_flex().flex_none().size(
-                                                                        IconSize::default().rems(),
-                                                                    )
-                                                                };
-                                                                match position {
-                                                                    IconPosition::Start => {
-                                                                        list_item
-                                                                            .start_slot(contents)
-                                                                    }
-                                                                    IconPosition::End => {
-                                                                        list_item.end_slot(contents)
-                                                                    }
-                                                                }
-                                                            },
-                                                        )
-                                                        .child(
-                                                            h_flex()
-                                                                .w_full()
-                                                                .justify_between()
-                                                                .child(label_element)
-                                                                .debug_selector(|| {
-                                                                    format!("MENU_ITEM-{}", label)
-                                                                })
-                                                                .children(
-                                                                    action.as_ref().and_then(
-                                                                        |action| {
-                                                                            self.action_context
-                                                                    .as_ref()
-                                                                    .map(|focus| {
-                                                                        KeyBinding::for_action_in(
-                                                                            &**action, focus,
-                                                                            window,
-                                                                        )
-                                                                    })
-                                                                    .unwrap_or_else(|| {
-                                                                        KeyBinding::for_action(
-                                                                            &**action, window,
-                                                                        )
-                                                                    })
-                                                                    .map(|binding| {
-                                                                        div().ml_4().child(binding)
-                                                                    })
-                                                                        },
-                                                                    ),
-                                                                ),
-                                                        )
-                                                        .on_click({
-                                                            let context =
-                                                                self.action_context.clone();
-                                                            move |_, window, cx| {
-                                                                handler(
-                                                                    context.as_ref(),
-                                                                    window,
-                                                                    cx,
-                                                                );
-                                                                menu.update(cx, |menu, cx| {
-                                                                    menu.clicked = true;
-                                                                    cx.emit(DismissEvent);
-                                                                })
-                                                                .ok();
-                                                            }
-                                                        }),
-                                                )
-                                                .into_any_element()
-                                        }
-                                        ContextMenuItem::CustomEntry {
-                                            entry_render,
-                                            handler,
-                                            selectable,
-                                        } => {
-                                            let handler = handler.clone();
-                                            let menu = cx.entity().downgrade();
-                                            let selectable = *selectable;
-                                            ListItem::new(ix)
-                                                .inset(true)
-                                                .toggle_state(if selectable {
-                                                    Some(ix) == self.selected_index
-                                                } else {
-                                                    false
+                                                        })
+                                                        .map(|binding| div().ml_4().child(binding))
+                                                })),
+                                        )
+                                        .on_click({
+                                            let context = self.action_context.clone();
+                                            move |_, window, cx| {
+                                                handler(context.as_ref(), window, cx);
+                                                menu.update(cx, |menu, cx| {
+                                                    menu.clicked = true;
+                                                    cx.emit(DismissEvent);
                                                 })
-                                                .selectable(selectable)
-                                                .when(selectable, |item| {
-                                                    item.on_click({
-                                                        let context = self.action_context.clone();
-                                                        move |_, window, cx| {
-                                                            handler(context.as_ref(), window, cx);
-                                                            menu.update(cx, |menu, cx| {
-                                                                menu.clicked = true;
-                                                                cx.emit(DismissEvent);
-                                                            })
-                                                            .ok();
-                                                        }
+                                                .ok();
+                                            }
+                                        })
+                                        .into_any_element()
+                                }
+                                ContextMenuItem::CustomEntry {
+                                    entry_render,
+                                    handler,
+                                    selectable,
+                                } => {
+                                    let handler = handler.clone();
+                                    let menu = cx.entity().downgrade();
+                                    let selectable = *selectable;
+                                    ListItem::new(ix)
+                                        .inset(true)
+                                        .toggle_state(if selectable {
+                                            Some(ix) == self.selected_index
+                                        } else {
+                                            false
+                                        })
+                                        .selectable(selectable)
+                                        .when(selectable, |item| {
+                                            item.on_click({
+                                                let context = self.action_context.clone();
+                                                move |_, window, cx| {
+                                                    handler(context.as_ref(), window, cx);
+                                                    menu.update(cx, |menu, cx| {
+                                                        menu.clicked = true;
+                                                        cx.emit(DismissEvent);
                                                     })
-                                                })
-                                                .child(entry_render(window, cx))
-                                                .into_any_element()
-                                        }
-                                    }
-                                },
-                            ))),
-                    ),
+                                                    .ok();
+                                                }
+                                            })
+                                        })
+                                        .child(entry_render(window, cx))
+                                        .into_any_element()
+                                }
+                            }
+                        },
+                    ))),
             )
     }
 }

crates/vim/src/vim.rs 🔗

@@ -1289,7 +1289,7 @@ impl Vim {
                     .map_or(false, |provider| provider.show_completions_in_normal_mode()),
                 _ => false,
             };
-            editor.set_show_inline_completions_enabled(enable_inline_completions, cx);
+            editor.set_inline_completions_enabled(enable_inline_completions, cx);
         });
         cx.notify()
     }

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -16,8 +16,8 @@ use gpui::{
 use search::{buffer_search, BufferSearchBar};
 use settings::{Settings, SettingsStore};
 use ui::{
-    prelude::*, ButtonStyle, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, IconName,
-    IconSize, PopoverMenu, PopoverMenuHandle, Tooltip,
+    prelude::*, ButtonStyle, ContextMenu, IconButton, IconButtonShape, IconName, IconSize,
+    PopoverMenu, PopoverMenuHandle, Tooltip,
 };
 use vim_mode_setting::VimModeSetting;
 use workspace::{
@@ -94,8 +94,7 @@ impl Render for QuickActionBar {
             git_blame_inline_enabled,
             show_git_blame_gutter,
             auto_signature_help_enabled,
-            show_inline_completions,
-            inline_completion_enabled,
+            inline_completions_enabled,
         ) = {
             let editor = editor.read(cx);
             let selection_menu_enabled = editor.selection_menu_enabled(cx);
@@ -104,8 +103,7 @@ impl Render for QuickActionBar {
             let git_blame_inline_enabled = editor.git_blame_inline_enabled();
             let show_git_blame_gutter = editor.show_git_blame_gutter();
             let auto_signature_help_enabled = editor.auto_signature_help_enabled(cx);
-            let show_inline_completions = editor.should_show_inline_completions(cx);
-            let inline_completion_enabled = editor.inline_completions_enabled(cx);
+            let inline_completions_enabled = editor.inline_completions_enabled(cx);
 
             (
                 selection_menu_enabled,
@@ -114,8 +112,7 @@ impl Render for QuickActionBar {
                 git_blame_inline_enabled,
                 show_git_blame_gutter,
                 auto_signature_help_enabled,
-                show_inline_completions,
-                inline_completion_enabled,
+                inline_completions_enabled,
             )
         };
 
@@ -297,12 +294,12 @@ impl Render for QuickActionBar {
                             },
                         );
 
-                        let mut inline_completion_entry = ContextMenuEntry::new("Edit Predictions")
-                            .toggleable(IconPosition::Start, inline_completion_enabled && show_inline_completions)
-                            .disabled(!inline_completion_enabled)
-                            .action(Some(
-                                editor::actions::ToggleInlineCompletions.boxed_clone(),
-                            )).handler({
+                        menu = menu.toggleable_entry(
+                            "Edit Predictions",
+                            inline_completions_enabled,
+                            IconPosition::Start,
+                            Some(editor::actions::ToggleInlineCompletions.boxed_clone()),
+                            {
                                 let editor = editor.clone();
                                 move |window, cx| {
                                     editor
@@ -315,14 +312,8 @@ impl Render for QuickActionBar {
                                         })
                                         .ok();
                                 }
-                            });
-                        if !inline_completion_enabled {
-                            inline_completion_entry = inline_completion_entry.documentation_aside(|_| {
-                                Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element()
-                            });
-                        }
-
-                        menu = menu.item(inline_completion_entry);
+                            },
+                        );
 
                         menu = menu.separator();
 

crates/zeta/src/zeta.rs 🔗

@@ -25,7 +25,8 @@ use gpui::{
 };
 use http_client::{HttpClient, Method};
 use language::{
-    Anchor, Buffer, BufferSnapshot, EditPreview, OffsetRangeExt, Point, ToOffset, ToPoint,
+    language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot, EditPreview,
+    OffsetRangeExt, Point, ToOffset, ToPoint,
 };
 use language_models::LlmApiToken;
 use postage::watch;
@@ -1456,11 +1457,15 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
 
     fn is_enabled(
         &self,
-        _buffer: &Entity<Buffer>,
-        _cursor_position: language::Anchor,
-        _cx: &App,
+        buffer: &Entity<Buffer>,
+        cursor_position: language::Anchor,
+        cx: &App,
     ) -> bool {
-        true
+        let buffer = buffer.read(cx);
+        let file = buffer.file();
+        let language = buffer.language_at(cursor_position);
+        let settings = all_language_settings(file, cx);
+        settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
     }
 
     fn needs_terms_acceptance(&self, cx: &App) -> bool {