Clarify copilot settings

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

assets/settings/default.json                |  13 +-
crates/copilot/src/copilot.rs               |   4 
crates/copilot_button/src/copilot_button.rs |  28 +++--
crates/editor/src/editor.rs                 |   5 
crates/settings/src/settings.rs             | 111 +++++++++-------------
5 files changed, 73 insertions(+), 88 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -1,6 +1,11 @@
 {
     // The name of the Zed theme to use for the UI
     "theme": "One Dark",
+    // Features that can be globally enabled or disabled
+    "features": {
+        // Show Copilot icon in status bar
+        "copilot": true
+    },
     // The name of a font to use for rendering text in the editor
     "buffer_font_family": "Zed Mono",
     // The OpenType features to enable for text in the editor.
@@ -13,11 +18,6 @@
     // The factor to grow the active pane by. Defaults to 1.0
     // which gives the same size as all other panes.
     "active_pane_magnification": 1.0,
-    // Enable / disable copilot integration.
-    "enable_copilot_integration": true,
-    // Controls whether copilot provides suggestion immediately
-    // or waits for a `copilot::Toggle`
-    "copilot": "on",
     // Whether to enable vim modes and key bindings
     "vim_mode": false,
     // Whether to show the informational hover box when moving the mouse
@@ -30,6 +30,9 @@
     // Whether to pop the completions menu while typing in an editor without
     // explicitly requesting it.
     "show_completions_on_input": true,
+    // Controls whether copilot provides suggestion immediately
+    // or waits for a `copilot::Toggle`
+    "show_copilot_suggestions": true,
     // Whether the screen sharing icon is shown in the os status bar.
     "show_call_status_icon": true,
     // Whether to use language servers to provide code intelligence.

crates/copilot/src/copilot.rs 🔗

@@ -172,7 +172,7 @@ impl Copilot {
             let http = http.clone();
             let node_runtime = node_runtime.clone();
             move |this, cx| {
-                if cx.global::<Settings>().enable_copilot_integration {
+                if cx.global::<Settings>().features.copilot {
                     if matches!(this.server, CopilotServer::Disabled) {
                         let start_task = cx
                             .spawn({
@@ -194,7 +194,7 @@ impl Copilot {
         })
         .detach();
 
-        if cx.global::<Settings>().enable_copilot_integration {
+        if cx.global::<Settings>().features.copilot {
             let start_task = cx
                 .spawn({
                     let http = http.clone();

crates/copilot_button/src/copilot_button.rs 🔗

@@ -50,15 +50,16 @@ pub fn init(cx: &mut AppContext) {
     cx.add_action(CopilotButton::deploy_copilot_menu);
     cx.add_action(
         |_: &mut CopilotButton, action: &ToggleCopilotForLanguage, cx| {
-            let language = action.language.to_owned();
-
-            let current_langauge = cx.global::<Settings>().copilot_on(Some(&language));
+            let language = action.language.clone();
+            let show_copilot_suggestions = cx
+                .global::<Settings>()
+                .show_copilot_suggestions(Some(&language));
 
             SettingsFile::update(cx, move |file_contents| {
                 file_contents.languages.insert(
-                    language.to_owned(),
+                    language,
                     settings::EditorSettings {
-                        copilot: Some((!current_langauge).into()),
+                        show_copilot_suggestions: Some((!show_copilot_suggestions).into()),
                         ..Default::default()
                     },
                 );
@@ -67,10 +68,9 @@ pub fn init(cx: &mut AppContext) {
     );
 
     cx.add_action(|_: &mut CopilotButton, _: &ToggleCopilotGlobally, cx| {
-        let copilot_on = cx.global::<Settings>().copilot_on(None);
-
+        let show_copilot_suggestions = cx.global::<Settings>().show_copilot_suggestions(None);
         SettingsFile::update(cx, move |file_contents| {
-            file_contents.editor.copilot = Some((!copilot_on).into())
+            file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
         })
     });
 }
@@ -94,7 +94,7 @@ impl View for CopilotButton {
     fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
         let settings = cx.global::<Settings>();
 
-        if !settings.enable_copilot_integration {
+        if !settings.features.copilot {
             return Empty::new().boxed();
         }
 
@@ -105,7 +105,9 @@ impl View for CopilotButton {
         };
         let status = copilot.read(cx).status();
 
-        let enabled = self.editor_enabled.unwrap_or(settings.copilot_on(None));
+        let enabled = self
+            .editor_enabled
+            .unwrap_or(settings.show_copilot_suggestions(None));
 
         let view_id = cx.view_id();
 
@@ -248,7 +250,7 @@ impl CopilotButton {
         let mut menu_options = Vec::with_capacity(6);
 
         if let Some(language) = &self.language {
-            let language_enabled = settings.copilot_on(Some(language.as_ref()));
+            let language_enabled = settings.show_copilot_suggestions(Some(language.as_ref()));
 
             menu_options.push(ContextMenuItem::item(
                 format!(
@@ -266,7 +268,7 @@ impl CopilotButton {
             ));
         }
 
-        let globally_enabled = cx.global::<Settings>().copilot_on(None);
+        let globally_enabled = cx.global::<Settings>().show_copilot_suggestions(None);
         menu_options.push(ContextMenuItem::item(
             if globally_enabled {
                 "Disable Copilot Globally"
@@ -319,7 +321,7 @@ impl CopilotButton {
 
         self.language = language_name.clone();
 
-        self.editor_enabled = Some(settings.copilot_on(language_name.as_deref()));
+        self.editor_enabled = Some(settings.show_copilot_suggestions(language_name.as_deref()));
 
         cx.notify()
     }

crates/editor/src/editor.rs 🔗

@@ -2816,7 +2816,10 @@ impl Editor {
         let snapshot = self.buffer.read(cx).snapshot(cx);
         let cursor = self.selections.newest_anchor().head();
         let language_name = snapshot.language_at(cursor).map(|language| language.name());
-        if !cx.global::<Settings>().copilot_on(language_name.as_deref()) {
+        if !cx
+            .global::<Settings>()
+            .show_copilot_suggestions(language_name.as_deref())
+        {
             self.hide_copilot_suggestion(cx);
             return None;
         }

crates/settings/src/settings.rs 🔗

@@ -28,11 +28,11 @@ pub use watched_json::watch_files;
 
 #[derive(Clone)]
 pub struct Settings {
+    pub features: Features,
     pub buffer_font_family_name: String,
     pub buffer_font_features: fonts::Features,
     pub buffer_font_family: FamilyId,
     pub default_buffer_font_size: f32,
-    pub enable_copilot_integration: bool,
     pub buffer_font_size: f32,
     pub active_pane_magnification: f32,
     pub cursor_blink: bool,
@@ -177,43 +177,7 @@ pub struct EditorSettings {
     pub ensure_final_newline_on_save: Option<bool>,
     pub formatter: Option<Formatter>,
     pub enable_language_server: Option<bool>,
-    #[schemars(skip)]
-    pub copilot: Option<OnOff>,
-}
-
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum OnOff {
-    On,
-    Off,
-}
-
-impl OnOff {
-    pub fn as_bool(&self) -> bool {
-        match self {
-            OnOff::On => true,
-            OnOff::Off => false,
-        }
-    }
-
-    pub fn from_bool(value: bool) -> OnOff {
-        match value {
-            true => OnOff::On,
-            false => OnOff::Off,
-        }
-    }
-}
-
-impl From<OnOff> for bool {
-    fn from(value: OnOff) -> bool {
-        value.as_bool()
-    }
-}
-
-impl From<bool> for OnOff {
-    fn from(value: bool) -> OnOff {
-        OnOff::from_bool(value)
-    }
+    pub show_copilot_suggestions: Option<bool>,
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -437,8 +401,7 @@ pub struct SettingsFileContent {
     #[serde(default)]
     pub base_keymap: Option<BaseKeymap>,
     #[serde(default)]
-    #[schemars(skip)]
-    pub enable_copilot_integration: Option<bool>,
+    pub features: FeaturesContent,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -447,6 +410,18 @@ pub struct LspSettings {
     pub initialization_options: Option<Value>,
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct Features {
+    pub copilot: bool,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct FeaturesContent {
+    pub copilot: Option<bool>,
+}
+
 impl Settings {
     /// Fill out the settings corresponding to the default.json file, overrides will be set later
     pub fn defaults(
@@ -500,7 +475,7 @@ impl Settings {
                 format_on_save: required(defaults.editor.format_on_save),
                 formatter: required(defaults.editor.formatter),
                 enable_language_server: required(defaults.editor.enable_language_server),
-                copilot: required(defaults.editor.copilot),
+                show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions),
             },
             editor_overrides: Default::default(),
             git: defaults.git.unwrap(),
@@ -517,7 +492,9 @@ impl Settings {
             telemetry_overrides: Default::default(),
             auto_update: defaults.auto_update.unwrap(),
             base_keymap: Default::default(),
-            enable_copilot_integration: defaults.enable_copilot_integration.unwrap(),
+            features: Features {
+                copilot: defaults.features.copilot.unwrap(),
+            },
         }
     }
 
@@ -569,10 +546,7 @@ impl Settings {
         merge(&mut self.autosave, data.autosave);
         merge(&mut self.default_dock_anchor, data.default_dock_anchor);
         merge(&mut self.base_keymap, data.base_keymap);
-        merge(
-            &mut self.enable_copilot_integration,
-            data.enable_copilot_integration,
-        );
+        merge(&mut self.features.copilot, data.features.copilot);
 
         self.editor_overrides = data.editor;
         self.git_overrides = data.git.unwrap_or_default();
@@ -596,12 +570,15 @@ impl Settings {
         self
     }
 
-    pub fn copilot_on(&self, language: Option<&str>) -> bool {
-        if self.enable_copilot_integration {
-            self.language_setting(language, |settings| settings.copilot.map(Into::into))
-        } else {
-            false
-        }
+    pub fn features(&self) -> &Features {
+        &self.features
+    }
+
+    pub fn show_copilot_suggestions(&self, language: Option<&str>) -> bool {
+        self.features.copilot
+            && self.language_setting(language, |settings| {
+                settings.show_copilot_suggestions.map(Into::into)
+            })
     }
 
     pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
@@ -740,7 +717,7 @@ impl Settings {
                 format_on_save: Some(FormatOnSave::On),
                 formatter: Some(Formatter::LanguageServer),
                 enable_language_server: Some(true),
-                copilot: Some(OnOff::On),
+                show_copilot_suggestions: Some(true),
             },
             editor_overrides: Default::default(),
             journal_defaults: Default::default(),
@@ -760,7 +737,7 @@ impl Settings {
             telemetry_overrides: Default::default(),
             auto_update: true,
             base_keymap: Default::default(),
-            enable_copilot_integration: true,
+            features: Features { copilot: true },
         }
     }
 
@@ -1125,7 +1102,7 @@ mod tests {
                 {
                     "language_overrides": {
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }
@@ -1135,7 +1112,7 @@ mod tests {
                 settings.languages.insert(
                     "Rust".into(),
                     EditorSettings {
-                        copilot: Some(OnOff::On),
+                        show_copilot_suggestions: Some(true),
                         ..Default::default()
                     },
                 );
@@ -1144,10 +1121,10 @@ mod tests {
                 {
                     "language_overrides": {
                         "Rust": {
-                            "copilot": "on"
+                            "show_copilot_suggestions": true
                         },
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }
@@ -1163,21 +1140,21 @@ mod tests {
                 {
                     "languages": {
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }
             "#
             .unindent(),
             |settings| {
-                settings.editor.copilot = Some(OnOff::On);
+                settings.editor.show_copilot_suggestions = Some(true);
             },
             r#"
                 {
-                    "copilot": "on",
+                    "show_copilot_suggestions": true,
                     "languages": {
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }
@@ -1187,13 +1164,13 @@ mod tests {
     }
 
     #[test]
-    fn test_update_langauge_copilot() {
+    fn test_update_language_copilot() {
         assert_new_settings(
             r#"
                 {
                     "languages": {
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }
@@ -1203,7 +1180,7 @@ mod tests {
                 settings.languages.insert(
                     "Rust".into(),
                     EditorSettings {
-                        copilot: Some(OnOff::On),
+                        show_copilot_suggestions: Some(true),
                         ..Default::default()
                     },
                 );
@@ -1212,10 +1189,10 @@ mod tests {
                 {
                     "languages": {
                         "Rust": {
-                            "copilot": "on"
+                            "show_copilot_suggestions": true
                         },
                         "JSON": {
-                            "copilot": "off"
+                            "show_copilot_suggestions": false
                         }
                     }
                 }