Add setting to allow disabling the Assistant (#9706)

This PR adds a new `assistant.enabled` setting that controls whether the
Zed Assistant is enabled.

Some users have requested the ability to disable the AI-related features
in Zed if they don't use them. Changing `assistant.enabled` to `false`
will hide the Assistant icon in the status bar (taking priority over the
`assistant.button` setting) as well as filter out the `assistant:`
actions.

The Assistant is enabled by default.

Release Notes:

- Added an `assistant.enabled` setting to control whether the Assistant
is enabled.
This commit is contained in:
Marshall Bowers 2024-03-22 11:55:29 -04:00 committed by GitHub
parent 4dc61f7ccd
commit c6d479715d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 100 additions and 37 deletions

1
Cargo.lock generated
View File

@ -318,6 +318,7 @@ dependencies = [
"chrono",
"client",
"collections",
"command_palette_hooks",
"ctor",
"editor",
"env_logger",

View File

@ -245,6 +245,8 @@
"assistant": {
// Version of this setting.
"version": "1",
// Whether the assistant is enabled.
"enabled": true,
// Whether to show the assistant panel button in the status bar.
"button": true,
// Where to dock the assistant panel. Can be 'left', 'right' or 'bottom'.

View File

@ -14,6 +14,7 @@ anyhow.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
editor.workspace = true
fs.workspace = true
futures.workspace = true

View File

@ -10,11 +10,12 @@ pub use assistant_panel::AssistantPanel;
use assistant_settings::{AssistantSettings, OpenAiModel, ZedDotDevModel};
use chrono::{DateTime, Local};
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
pub(crate) use completion_provider::*;
use gpui::{actions, AppContext, SharedString};
use gpui::{actions, AppContext, Global, SharedString};
pub(crate) use saved_conversation::*;
use serde::{Deserialize, Serialize};
use settings::Settings;
use settings::{Settings, SettingsStore};
use std::{
fmt::{self, Display},
sync::Arc,
@ -182,10 +183,61 @@ enum MessageStatus {
Error(SharedString),
}
/// The state pertaining to the Assistant.
#[derive(Default)]
struct Assistant {
/// Whether the Assistant is enabled.
enabled: bool,
}
impl Global for Assistant {}
impl Assistant {
const NAMESPACE: &'static str = "assistant";
fn set_enabled(&mut self, enabled: bool, cx: &mut AppContext) {
if self.enabled == enabled {
return;
}
self.enabled = enabled;
if !enabled {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(Self::NAMESPACE);
});
return;
}
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.show_namespace(Self::NAMESPACE);
});
}
}
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
cx.set_global(Assistant::default());
AssistantSettings::register(cx);
completion_provider::init(client, cx);
assistant_panel::init(cx);
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(Assistant::NAMESPACE);
});
cx.update_global(|assistant: &mut Assistant, cx: &mut AppContext| {
let settings = AssistantSettings::get_global(cx);
assistant.set_enabled(settings.enabled, cx);
});
cx.observe_global::<SettingsStore>(|cx| {
cx.update_global(|assistant: &mut Assistant, cx: &mut AppContext| {
let settings = AssistantSettings::get_global(cx);
assistant.set_enabled(settings.enabled, cx);
});
})
.detach();
}
#[cfg(test)]

View File

@ -55,6 +55,11 @@ pub fn init(cx: &mut AppContext) {
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
workspace
.register_action(|workspace, _: &ToggleFocus, cx| {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled {
return;
}
workspace.toggle_panel_focus::<AssistantPanel>(cx);
})
.register_action(AssistantPanel::inline_assist)
@ -229,6 +234,11 @@ impl AssistantPanel {
_: &InlineAssist,
cx: &mut ViewContext<Workspace>,
) {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled {
return;
}
let Some(assistant) = workspace.panel::<AssistantPanel>(cx) else {
return;
};
@ -1217,7 +1227,12 @@ impl Panel for AssistantPanel {
}
fn icon(&self, cx: &WindowContext) -> Option<IconName> {
Some(IconName::Ai).filter(|_| AssistantSettings::get_global(cx).button)
let settings = AssistantSettings::get_global(cx);
if !settings.enabled || !settings.button {
return None;
}
Some(IconName::Ai)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {

View File

@ -160,6 +160,7 @@ fn open_ai_url() -> String {
#[derive(Default, Debug, Deserialize, Serialize)]
pub struct AssistantSettings {
pub enabled: bool,
pub button: bool,
pub dock: AssistantDockPosition,
pub default_width: Pixels,
@ -201,42 +202,26 @@ impl AssistantSettingsContent {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => settings.clone(),
},
AssistantSettingsContent::Legacy(settings) => {
if let Some(open_ai_api_url) = settings.openai_api_url.as_ref() {
AssistantSettingsContentV1 {
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
provider: Some(AssistantProvider::OpenAi {
default_model: settings
.default_open_ai_model
.clone()
.unwrap_or_default(),
api_url: open_ai_api_url.clone(),
}),
}
} else if let Some(open_ai_model) = settings.default_open_ai_model.clone() {
AssistantSettingsContentV1 {
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
provider: Some(AssistantProvider::OpenAi {
AssistantSettingsContent::Legacy(settings) => AssistantSettingsContentV1 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
provider: if let Some(open_ai_api_url) = settings.openai_api_url.as_ref() {
Some(AssistantProvider::OpenAi {
default_model: settings.default_open_ai_model.clone().unwrap_or_default(),
api_url: open_ai_api_url.clone(),
})
} else {
settings.default_open_ai_model.clone().map(|open_ai_model| {
AssistantProvider::OpenAi {
default_model: open_ai_model,
api_url: open_ai_url(),
}),
}
} else {
AssistantSettingsContentV1 {
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
provider: None,
}
}
}
}
})
},
},
}
}
@ -264,6 +249,7 @@ pub enum VersionedAssistantSettingsContent {
impl Default for VersionedAssistantSettingsContent {
fn default() -> Self {
Self::V1(AssistantSettingsContentV1 {
enabled: None,
button: None,
dock: None,
default_width: None,
@ -275,6 +261,10 @@ impl Default for VersionedAssistantSettingsContent {
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV1 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
@ -340,6 +330,7 @@ impl Settings for AssistantSettings {
for value in [default_value].iter().chain(user_values) {
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
merge(

View File

@ -3073,6 +3073,7 @@ mod tests {
notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx);
workspace::init(app_state.clone(), cx);
Project::init_settings(cx);
command_palette::init(cx);
language::init(cx);
editor::init(cx);
project_panel::init_settings(cx);