From 7c5bc3c26fcc368fccc3c635876a14afc53ca0a0 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Apr 2024 19:17:12 -0400 Subject: [PATCH] Add the ability for extensions to provide language settings (#10296) This PR adds the ability for extensions to provide certain language settings via the language `config.toml`. These settings are then merged in with the rest of the settings when the language is loaded from the extension. The language settings that are available are: - `tab_size` - `hard_tabs` - `soft_wrap` Additionally, for bundled languages we moved these settings out of the `settings/default.json` and into their respective `config.toml`s . For languages currently provided by extensions, we are leaving the values in the `settings/default.json` temporarily until all released versions of Zed are able to load these settings from the extension. --- Along the way we ended up refactoring the `Settings::load` method slightly, introducing a new `SettingsSources` struct to better convey where the settings are being loaded from. This makes it easier to load settings from specific locations/sets of locations in an explicit way. Release Notes: - N/A --------- Co-authored-by: Max Co-authored-by: Max Brunsfeld --- assets/settings/default.json | 36 --- crates/assistant/src/assistant_settings.rs | 7 +- crates/auto_update/src/auto_update.rs | 13 +- crates/call/src/call_settings.rs | 13 +- crates/client/src/client.rs | 28 +- crates/collab_ui/src/panel_settings.rs | 30 ++- .../src/project_diagnostics_settings.rs | 16 +- crates/editor/src/editor_settings.rs | 10 +- crates/extension/src/extension_settings.rs | 13 +- crates/journal/src/journal.rs | 10 +- crates/language/src/language.rs | 18 ++ crates/language/src/language_registry.rs | 17 ++ crates/language/src/language_settings.rs | 26 +- crates/languages/src/deno.rs | 14 +- crates/languages/src/elixir.rs | 15 +- crates/languages/src/elixir/config.toml | 1 + crates/languages/src/go/config.toml | 2 + crates/languages/src/javascript/config.toml | 1 + crates/languages/src/json/config.toml | 1 + crates/languages/src/lib.rs | 28 +- crates/languages/src/markdown/config.toml | 3 + .../languages/src/ocaml-interface/config.toml | 1 + crates/languages/src/ocaml/config.toml | 1 + crates/languages/src/terraform/config.toml | 1 + crates/languages/src/tsx/config.toml | 1 + crates/languages/src/typescript/config.toml | 1 + crates/languages/src/yaml/config.toml | 1 + crates/project/src/project_settings.rs | 7 +- .../src/project_panel_settings.rs | 7 +- crates/settings/src/settings.rs | 4 +- crates/settings/src/settings_store.rs | 241 +++++++++++------- crates/tasks_ui/src/settings.rs | 16 +- crates/terminal/src/terminal_settings.rs | 8 +- crates/theme/src/settings.rs | 17 +- crates/vim/src/vim.rs | 20 +- crates/welcome/src/base_keymap_setting.rs | 18 +- crates/workspace/src/item.rs | 10 +- crates/workspace/src/workspace_settings.rs | 20 +- crates/worktree/src/worktree_settings.rs | 7 +- crates/zed/src/zed.rs | 2 + extensions/gleam/languages/gleam/config.toml | 1 + .../prisma/languages/prisma/config.toml | 1 + 42 files changed, 349 insertions(+), 338 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 83f6fb55e5..19c1357f22 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -556,18 +556,10 @@ "C": { "format_on_save": "off" }, - "Plain Text": { - "soft_wrap": "preferred_line_length" - }, - "Elixir": { - "tab_size": 2 - }, "Gleam": { "tab_size": 2 }, "Go": { - "tab_size": 4, - "hard_tabs": true, "code_actions_on_format": { "source.organizeImports": true } @@ -575,34 +567,6 @@ "Make": { "hard_tabs": true }, - "Markdown": { - "tab_size": 2, - "soft_wrap": "preferred_line_length" - }, - "JavaScript": { - "tab_size": 2 - }, - "Terraform": { - "tab_size": 2 - }, - "TypeScript": { - "tab_size": 2 - }, - "TSX": { - "tab_size": 2 - }, - "YAML": { - "tab_size": 2 - }, - "JSON": { - "tab_size": 2 - }, - "OCaml": { - "tab_size": 2 - }, - "OCaml Interface": { - "tab_size": 2 - }, "Prisma": { "tab_size": 2 } diff --git a/crates/assistant/src/assistant_settings.rs b/crates/assistant/src/assistant_settings.rs index fb7060a932..d29cd24eea 100644 --- a/crates/assistant/src/assistant_settings.rs +++ b/crates/assistant/src/assistant_settings.rs @@ -10,7 +10,7 @@ use serde::{ de::{self, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Clone, Debug, Default, PartialEq)] pub enum ZedDotDevModel { @@ -332,13 +332,12 @@ impl Settings for AssistantSettings { type FileContent = AssistantSettingsContent; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { let mut settings = AssistantSettings::default(); - for value in [default_value].iter().chain(user_values) { + for value in sources.defaults_and_customizations() { let value = value.upgrade(); merge(&mut settings.enabled, value.enabled); merge(&mut settings.button, value.button); diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 51e2f23567..7ba1a55fb4 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -17,7 +17,7 @@ use serde::Deserialize; use serde_derive::Serialize; use smol::io::AsyncReadExt; -use settings::{Settings, SettingsStore}; +use settings::{Settings, SettingsSources, SettingsStore}; use smol::{fs::File, process::Command}; use release_channel::{AppCommitSha, AppVersion, ReleaseChannel}; @@ -91,13 +91,12 @@ impl Settings for AutoUpdateSetting { type FileContent = AutoUpdateSettingOverride; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { Ok(Self( - Self::json_merge(default_value, user_values)? + sources + .release_channel + .or(sources.user) + .unwrap_or(sources.default) .0 .ok_or_else(Self::missing_default)?, )) diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index 6aa4253689..446178ffb9 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -2,7 +2,7 @@ use anyhow::Result; use gpui::AppContext; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Deserialize, Debug)] pub struct CallSettings { @@ -29,14 +29,7 @@ impl Settings for CallSettings { type FileContent = CallSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _cx: &mut AppContext, - ) -> Result - where - Self: Sized, - { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index fa0733fb74..157b7ce425 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -28,7 +28,7 @@ use release_channel::{AppVersion, ReleaseChannel}; use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::{Settings, SettingsSources, SettingsStore}; use std::fmt; use std::{ any::TypeId, @@ -97,15 +97,11 @@ impl Settings for ClientSettings { type FileContent = ClientSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result + fn load(sources: SettingsSources, _: &mut AppContext) -> Result where Self: Sized, { - let mut result = Self::load_via_json_merge(default_value, user_values)?; + let mut result = sources.json_merge::()?; if let Some(server_url) = &*ZED_SERVER_URL { result.server_url = server_url.clone() } @@ -427,21 +423,19 @@ impl settings::Settings for TelemetrySettings { type FileContent = TelemetrySettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { Ok(Self { - diagnostics: user_values.first().and_then(|v| v.diagnostics).unwrap_or( - default_value + diagnostics: sources.user.as_ref().and_then(|v| v.diagnostics).unwrap_or( + sources + .default .diagnostics .ok_or_else(Self::missing_default)?, ), - metrics: user_values - .first() + metrics: sources + .user + .as_ref() .and_then(|v| v.metrics) - .unwrap_or(default_value.metrics.ok_or_else(Self::missing_default)?), + .unwrap_or(sources.default.metrics.ok_or_else(Self::missing_default)?), }) } } diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index fefd29696c..f82cba9e0b 100644 --- a/crates/collab_ui/src/panel_settings.rs +++ b/crates/collab_ui/src/panel_settings.rs @@ -2,7 +2,7 @@ use anyhow; use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; use workspace::dock::DockPosition; #[derive(Deserialize, Debug)] @@ -53,48 +53,52 @@ pub struct MessageEditorSettings { impl Settings for CollaborationPanelSettings { const KEY: Option<&'static str> = Some("collaboration_panel"); + type FileContent = PanelSettingsContent; + fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } impl Settings for ChatPanelSettings { const KEY: Option<&'static str> = Some("chat_panel"); + type FileContent = PanelSettingsContent; + fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } impl Settings for NotificationPanelSettings { const KEY: Option<&'static str> = Some("notification_panel"); + type FileContent = PanelSettingsContent; + fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } impl Settings for MessageEditorSettings { const KEY: Option<&'static str> = Some("message_editor"); + type FileContent = MessageEditorSettings; + fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } diff --git a/crates/diagnostics/src/project_diagnostics_settings.rs b/crates/diagnostics/src/project_diagnostics_settings.rs index d0feeeb3a7..55879d0c42 100644 --- a/crates/diagnostics/src/project_diagnostics_settings.rs +++ b/crates/diagnostics/src/project_diagnostics_settings.rs @@ -1,5 +1,8 @@ +use anyhow::Result; +use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use settings::{Settings, SettingsSources}; #[derive(Deserialize, Debug)] pub struct ProjectDiagnosticsSettings { @@ -15,18 +18,11 @@ pub struct ProjectDiagnosticsSettingsContent { include_warnings: Option, } -impl settings::Settings for ProjectDiagnosticsSettings { +impl Settings for ProjectDiagnosticsSettings { const KEY: Option<&'static str> = Some("diagnostics"); type FileContent = ProjectDiagnosticsSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _cx: &mut gpui::AppContext, - ) -> anyhow::Result - where - Self: Sized, - { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index bf960dc751..9a1e072731 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -1,6 +1,7 @@ +use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Deserialize, Clone)] pub struct EditorSettings { @@ -224,10 +225,9 @@ impl Settings for EditorSettings { type FileContent = EditorSettingsContent; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut gpui::AppContext, + sources: SettingsSources, + _: &mut AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } diff --git a/crates/extension/src/extension_settings.rs b/crates/extension/src/extension_settings.rs index ea87d04e8a..42ee34930c 100644 --- a/crates/extension/src/extension_settings.rs +++ b/crates/extension/src/extension_settings.rs @@ -3,7 +3,7 @@ use collections::HashMap; use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; use std::sync::Arc; #[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] @@ -26,14 +26,7 @@ impl Settings for ExtensionSettings { type FileContent = Self; - fn load( - _default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _cx: &mut AppContext, - ) -> Result - where - Self: Sized, - { - Ok(user_values.get(0).copied().cloned().unwrap_or_default()) + fn load(sources: SettingsSources, _cx: &mut AppContext) -> Result { + Ok(sources.user.cloned().unwrap_or_default()) } } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 386b2eb606..ebb4f4b9a2 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -5,7 +5,7 @@ use editor::Editor; use gpui::{actions, AppContext, ViewContext, WindowContext}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; use std::{ fs::OpenOptions, path::{Path, PathBuf}, @@ -50,12 +50,8 @@ impl settings::Settings for JournalSettings { type FileContent = Self; - fn load( - defaults: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(defaults, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index fdbd4e2d4b..65c838cda0 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -38,6 +38,7 @@ use schemars::{ use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use smol::future::FutureExt as _; +use std::num::NonZeroU32; use std::{ any::Any, cell::RefCell, @@ -73,6 +74,8 @@ pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer}; pub use text::LineEnding; pub use tree_sitter::{Parser, Tree}; +use crate::language_settings::SoftWrap; + /// Initializes the `language` crate. /// /// This should be called before making use of items from the create. @@ -99,6 +102,7 @@ lazy_static! { pub static ref PLAIN_TEXT: Arc = Arc::new(Language::new( LanguageConfig { name: "Plain Text".into(), + soft_wrap: Some(SoftWrap::PreferredLineLength), ..Default::default() }, None, @@ -576,6 +580,17 @@ pub struct LanguageConfig { /// The names of any Prettier plugins that should be used for this language. #[serde(default)] pub prettier_plugins: Vec>, + + /// Whether to indent lines using tab characters, as opposed to multiple + /// spaces. + #[serde(default)] + pub hard_tabs: Option, + /// How many columns a tab should occupy. + #[serde(default)] + pub tab_size: Option, + /// How to soft-wrap long lines of text. + #[serde(default)] + pub soft_wrap: Option, } #[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)] @@ -660,6 +675,9 @@ impl Default for LanguageConfig { prettier_parser_name: None, prettier_plugins: Default::default(), collapsed_placeholder: Default::default(), + hard_tabs: Default::default(), + tab_size: Default::default(), + soft_wrap: Default::default(), } } } diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 11e7bc796b..c1346f8ee3 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -1,3 +1,4 @@ +use crate::language_settings::{AllLanguageSettingsContent, LanguageSettingsContent}; use crate::{ language_settings::all_language_settings, task_context::ContextProvider, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher, LanguageServerName, LspAdapter, @@ -38,6 +39,7 @@ pub struct LanguageRegistry { struct LanguageRegistryState { next_language_server_id: usize, languages: Vec>, + language_settings: AllLanguageSettingsContent, available_languages: Vec, grammars: HashMap, AvailableGrammar>, lsp_adapters: HashMap, Vec>>, @@ -145,6 +147,7 @@ impl LanguageRegistry { languages: Vec::new(), available_languages: Vec::new(), grammars: Default::default(), + language_settings: Default::default(), loading_languages: Default::default(), lsp_adapters: Default::default(), subscription: watch::channel(), @@ -338,6 +341,10 @@ impl LanguageRegistry { *state.subscription.0.borrow_mut() = (); } + pub fn language_settings(&self) -> AllLanguageSettingsContent { + self.state.read().language_settings.clone() + } + pub fn language_names(&self) -> Vec { let state = self.state.read(); let mut result = state @@ -854,6 +861,16 @@ impl LanguageRegistryState { if let Some(theme) = self.theme.as_ref() { language.set_theme(theme.syntax()); } + self.language_settings.languages.insert( + language.name(), + LanguageSettingsContent { + tab_size: language.config.tab_size, + hard_tabs: language.config.hard_tabs, + soft_wrap: language.config.soft_wrap, + ..Default::default() + } + .clone(), + ); self.languages.push(language); self.version += 1; *self.subscription.0.borrow_mut() = (); diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index ad1c5603a2..3672fb1e15 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -10,7 +10,7 @@ use schemars::{ JsonSchema, }; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsLocation}; +use settings::{Settings, SettingsLocation, SettingsSources}; use std::{num::NonZeroU32, path::Path, sync::Arc}; impl<'a> Into> for &'a dyn File { @@ -119,7 +119,7 @@ pub struct CopilotSettings { } /// The settings for all languages. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AllLanguageSettingsContent { /// The settings for enabling/disabling features. #[serde(default)] @@ -140,7 +140,7 @@ pub struct AllLanguageSettingsContent { } /// The settings for a particular language. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct LanguageSettingsContent { /// How many columns a tab should occupy. /// @@ -249,7 +249,7 @@ pub struct LanguageSettingsContent { } /// The contents of the GitHub Copilot settings. -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)] pub struct CopilotSettingsContent { /// A list of globs representing files that Copilot should be disabled for. #[serde(default)] @@ -257,7 +257,7 @@ pub struct CopilotSettingsContent { } /// The settings for enabling/disabling features. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct FeaturesContent { /// Whether the GitHub Copilot feature is enabled. @@ -473,11 +473,9 @@ impl settings::Settings for AllLanguageSettings { type FileContent = AllLanguageSettingsContent; - fn load( - default_value: &Self::FileContent, - user_settings: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + let default_value = sources.default; + // A default is provided for all settings. let mut defaults: LanguageSettings = serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?; @@ -500,7 +498,8 @@ impl settings::Settings for AllLanguageSettings { .and_then(|c| c.disabled_globs.as_ref()) .ok_or_else(Self::missing_default)?; - for user_settings in user_settings { + let mut file_types: HashMap, Vec> = HashMap::default(); + for user_settings in sources.customizations() { if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) { copilot_enabled = copilot; } @@ -528,11 +527,8 @@ impl settings::Settings for AllLanguageSettings { user_language_settings, ); } - } - let mut file_types: HashMap, Vec> = HashMap::default(); - for user_file_types in user_settings.iter().map(|s| &s.file_types) { - for (language, suffixes) in user_file_types { + for (language, suffixes) in &user_settings.file_types { file_types .entry(language.clone()) .or_default() diff --git a/crates/languages/src/deno.rs b/crates/languages/src/deno.rs index fc7fd49d02..b43411c674 100644 --- a/crates/languages/src/deno.rs +++ b/crates/languages/src/deno.rs @@ -2,12 +2,13 @@ use anyhow::{anyhow, bail, Context, Result}; use async_trait::async_trait; use collections::HashMap; use futures::StreamExt; +use gpui::AppContext; use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::{CodeActionKind, LanguageServerBinary}; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use serde_json::json; -use settings::Settings; +use settings::{Settings, SettingsSources}; use smol::{fs, fs::File}; use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc}; use util::{ @@ -31,15 +32,8 @@ impl Settings for DenoSettings { type FileContent = DenoSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut gpui::AppContext, - ) -> Result - where - Self: Sized, - { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/languages/src/elixir.rs b/crates/languages/src/elixir.rs index 26f7f3e47e..ac8d55028d 100644 --- a/crates/languages/src/elixir.rs +++ b/crates/languages/src/elixir.rs @@ -1,14 +1,14 @@ use anyhow::{anyhow, bail, Context, Result}; use async_trait::async_trait; use futures::StreamExt; -use gpui::{AsyncAppContext, Task}; +use gpui::{AppContext, AsyncAppContext, Task}; pub use language::*; use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind}; use project::project_settings::ProjectSettings; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use serde_json::Value; -use settings::Settings; +use settings::{Settings, SettingsSources}; use smol::fs::{self, File}; use std::{ any::Any, @@ -56,15 +56,8 @@ impl Settings for ElixirSettings { type FileContent = ElixirSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut gpui::AppContext, - ) -> Result - where - Self: Sized, - { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/languages/src/elixir/config.toml b/crates/languages/src/elixir/config.toml index 81e92d45b0..a0673089dd 100644 --- a/crates/languages/src/elixir/config.toml +++ b/crates/languages/src/elixir/config.toml @@ -10,6 +10,7 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] }, { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, ] +tab_size = 2 scope_opt_in_language_servers = ["tailwindcss-language-server"] [overrides.string] diff --git a/crates/languages/src/go/config.toml b/crates/languages/src/go/config.toml index cf29bfbb23..2e3512dfbb 100644 --- a/crates/languages/src/go/config.toml +++ b/crates/languages/src/go/config.toml @@ -11,3 +11,5 @@ brackets = [ { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] }, { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] }, ] +tab_size = 4 +hard_tabs = true diff --git a/crates/languages/src/javascript/config.toml b/crates/languages/src/javascript/config.toml index a3acd1aac0..bbfec1213b 100644 --- a/crates/languages/src/javascript/config.toml +++ b/crates/languages/src/javascript/config.toml @@ -15,6 +15,7 @@ brackets = [ { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] }, ] word_characters = ["$", "#"] +tab_size = 2 scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "babel" diff --git a/crates/languages/src/json/config.toml b/crates/languages/src/json/config.toml index 34b3bc8bc6..c4e0ace9bf 100644 --- a/crates/languages/src/json/config.toml +++ b/crates/languages/src/json/config.toml @@ -9,3 +9,4 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, ] prettier_parser_name = "json" +tab_size = 2 diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index fe482a7089..cd80d5122f 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -1,11 +1,12 @@ use anyhow::Context; -use gpui::AppContext; +use gpui::{AppContext, BorrowAppContext}; pub use language::*; use node_runtime::NodeRuntime; use rust_embed::RustEmbed; -use settings::Settings; +use settings::{Settings, SettingsStore}; +use smol::stream::StreamExt; use std::{str, sync::Arc}; -use util::asset_str; +use util::{asset_str, ResultExt}; use crate::{elixir::elixir_task_context, rust::RustContextProvider}; @@ -327,6 +328,27 @@ pub fn init( "Svelte".into(), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ); + + let mut subscription = languages.subscribe(); + let mut prev_language_settings = languages.language_settings(); + + cx.spawn(|cx| async move { + while subscription.next().await.is_some() { + let language_settings = languages.language_settings(); + if language_settings != prev_language_settings { + cx.update(|cx| { + cx.update_global(|settings: &mut SettingsStore, cx| { + settings + .set_extension_settings(language_settings.clone(), cx) + .log_err(); + }); + })?; + prev_language_settings = language_settings; + } + } + anyhow::Ok(()) + }) + .detach(); } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/languages/src/markdown/config.toml b/crates/languages/src/markdown/config.toml index 70633128d8..e334e86d6a 100644 --- a/crates/languages/src/markdown/config.toml +++ b/crates/languages/src/markdown/config.toml @@ -12,3 +12,6 @@ brackets = [ { start = "`", end = "`", close = false, newline = false }, ] prettier_parser_name = "markdown" + +tab_size = 2 +soft_wrap = "preferred_line_length" diff --git a/crates/languages/src/ocaml-interface/config.toml b/crates/languages/src/ocaml-interface/config.toml index 9ce77acb6b..a1efdf18df 100644 --- a/crates/languages/src/ocaml-interface/config.toml +++ b/crates/languages/src/ocaml-interface/config.toml @@ -10,3 +10,4 @@ brackets = [ { start = "[", end = "]", close = true, newline = true }, { start = "(", end = ")", close = true, newline = true } ] +tab_size = 2 diff --git a/crates/languages/src/ocaml/config.toml b/crates/languages/src/ocaml/config.toml index 35e452ade0..2cd81bde52 100644 --- a/crates/languages/src/ocaml/config.toml +++ b/crates/languages/src/ocaml/config.toml @@ -11,3 +11,4 @@ brackets = [ { start = "(", end = ")", close = true, newline = true }, { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] } ] +tab_size = 2 diff --git a/crates/languages/src/terraform/config.toml b/crates/languages/src/terraform/config.toml index c2401c24ca..cfb27f4cc2 100644 --- a/crates/languages/src/terraform/config.toml +++ b/crates/languages/src/terraform/config.toml @@ -12,3 +12,4 @@ brackets = [ { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] }, { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] }, ] +tab_size = 2 diff --git a/crates/languages/src/tsx/config.toml b/crates/languages/src/tsx/config.toml index 666c55d9a7..044fc15d81 100644 --- a/crates/languages/src/tsx/config.toml +++ b/crates/languages/src/tsx/config.toml @@ -16,6 +16,7 @@ brackets = [ word_characters = ["#", "$"] scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "typescript" +tab_size = 2 [overrides.element] line_comments = { remove = true } diff --git a/crates/languages/src/typescript/config.toml b/crates/languages/src/typescript/config.toml index 8a9f2bc8f0..ca115622c6 100644 --- a/crates/languages/src/typescript/config.toml +++ b/crates/languages/src/typescript/config.toml @@ -15,3 +15,4 @@ brackets = [ ] word_characters = ["#", "$"] prettier_parser_name = "typescript" +tab_size = 2 diff --git a/crates/languages/src/yaml/config.toml b/crates/languages/src/yaml/config.toml index a4588275c1..43b21a1329 100644 --- a/crates/languages/src/yaml/config.toml +++ b/crates/languages/src/yaml/config.toml @@ -11,3 +11,4 @@ brackets = [ increase_indent_pattern = ":\\s*[|>]?\\s*$" prettier_parser_name = "yaml" +tab_size = 2 diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index a7a9eafa0e..79642646c4 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -2,7 +2,7 @@ use collections::HashMap; use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; use std::sync::Arc; #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] @@ -61,10 +61,9 @@ impl Settings for ProjectSettings { type FileContent = Self; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index 5285684891..e23d152a8f 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -2,7 +2,7 @@ use anyhow; use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -62,10 +62,9 @@ impl Settings for ProjectPanelSettings { type FileContent = ProjectPanelSettingsContent; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 4febec6542..e646e37f2c 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -8,7 +8,9 @@ use util::asset_str; pub use keymap_file::KeymapFile; pub use settings_file::*; -pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsStore}; +pub use settings_store::{ + Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsSources, SettingsStore, +}; #[derive(RustEmbed)] #[folder = "../../assets"] diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 8b5ac47389..cdc2640092 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -29,14 +29,7 @@ pub trait Settings: 'static + Send + Sync { /// The logic for combining together values from one or more JSON files into the /// final value for this setting. - /// - /// The user values are ordered from least specific (the global settings file) - /// to most specific (the innermost local settings file). - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - cx: &mut AppContext, - ) -> Result + fn load(sources: SettingsSources, cx: &mut AppContext) -> Result where Self: Sized; @@ -48,31 +41,6 @@ pub trait Settings: 'static + Send + Sync { generator.root_schema_for::() } - fn json_merge( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - ) -> Result { - let mut merged = serde_json::Value::Null; - for value in [default_value].iter().chain(user_values) { - merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); - } - Ok(serde_json::from_value(merged)?) - } - - fn load_via_json_merge( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - ) -> Result - where - Self: DeserializeOwned, - { - let mut merged = serde_json::Value::Null; - for value in [default_value].iter().chain(user_values) { - merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); - } - Ok(serde_json::from_value(merged)?) - } - fn missing_default() -> anyhow::Error { anyhow::anyhow!("missing default") } @@ -119,6 +87,48 @@ pub trait Settings: 'static + Send + Sync { } } +#[derive(Clone, Copy, Debug)] +pub struct SettingsSources<'a, T> { + /// The default Zed settings. + pub default: &'a T, + /// Settings provided by extensions. + pub extensions: Option<&'a T>, + /// The user settings. + pub user: Option<&'a T>, + /// The user settings for the current release channel. + pub release_channel: Option<&'a T>, + /// The project settings, ordered from least specific to most specific. + pub project: &'a [&'a T], +} + +impl<'a, T: Serialize> SettingsSources<'a, T> { + /// Returns an iterator over the default settings as well as all settings customizations. + pub fn defaults_and_customizations(&self) -> impl Iterator { + [self.default].into_iter().chain(self.customizations()) + } + + /// Returns an iterator over all of the settings customizations. + pub fn customizations(&self) -> impl Iterator { + self.extensions + .into_iter() + .chain(self.user) + .chain(self.release_channel) + .chain(self.project.iter().copied()) + } + + /// Returns the settings after performing a JSON merge of the customizations into the + /// default settings. + /// + /// More-specific customizations win out over the less-specific ones. + pub fn json_merge(&self) -> Result { + let mut merged = serde_json::Value::Null; + for value in self.defaults_and_customizations() { + merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); + } + Ok(serde_json::from_value(merged)?) + } +} + #[derive(Clone, Copy)] pub struct SettingsLocation<'a> { pub worktree_id: usize, @@ -136,6 +146,7 @@ pub struct SettingsStore { setting_values: HashMap>, raw_default_settings: serde_json::Value, raw_user_settings: serde_json::Value, + raw_extension_settings: serde_json::Value, raw_local_settings: BTreeMap<(usize, Arc), serde_json::Value>, tab_size_callback: Option<( TypeId, @@ -151,6 +162,7 @@ impl Default for SettingsStore { setting_values: Default::default(), raw_default_settings: serde_json::json!({}), raw_user_settings: serde_json::json!({}), + raw_extension_settings: serde_json::json!({}), raw_local_settings: Default::default(), tab_size_callback: Default::default(), } @@ -169,8 +181,7 @@ trait AnySettingValue: 'static + Send + Sync { fn deserialize_setting(&self, json: &serde_json::Value) -> Result; fn load_setting( &self, - default_value: &DeserializedSetting, - custom: &[DeserializedSetting], + sources: SettingsSources, cx: &mut AppContext, ) -> Result>; fn value_for_path(&self, path: Option) -> &dyn Any; @@ -204,29 +215,35 @@ impl SettingsStore { .deserialize_setting(&self.raw_default_settings) .log_err() { - let mut user_values_stack = Vec::new(); - - if let Some(user_settings) = setting_value + let user_value = setting_value .deserialize_setting(&self.raw_user_settings) - .log_err() - { - user_values_stack = vec![user_settings]; - } + .log_err(); + let mut release_channel_value = None; if let Some(release_settings) = &self .raw_user_settings .get(release_channel::RELEASE_CHANNEL.dev_name()) { - if let Some(release_settings) = setting_value + release_channel_value = setting_value .deserialize_setting(release_settings) - .log_err() - { - user_values_stack.push(release_settings); - } + .log_err(); } + let extension_value = setting_value + .deserialize_setting(&self.raw_extension_settings) + .log_err(); + if let Some(setting) = setting_value - .load_setting(&default_settings, &user_values_stack, cx) + .load_setting( + SettingsSources { + default: &default_settings, + release_channel: release_channel_value.as_ref(), + extensions: extension_value.as_ref(), + user: user_value.as_ref(), + project: &[], + }, + cx, + ) .context("A default setting must be added to the `default.json` file") .log_err() { @@ -425,6 +442,21 @@ impl SettingsStore { Ok(()) } + pub fn set_extension_settings( + &mut self, + content: T, + cx: &mut AppContext, + ) -> Result<()> { + let settings: serde_json::Value = serde_json::to_value(content)?; + if settings.is_object() { + self.raw_extension_settings = settings; + self.recompute_values(None, cx)?; + Ok(()) + } else { + Err(anyhow!("settings must be an object")) + } + } + /// Add or remove a set of local settings via a JSON string. pub fn clear_local_settings(&mut self, root_id: usize, cx: &mut AppContext) -> Result<()> { self.raw_local_settings.retain(|k, _| k.0 != root_id); @@ -551,22 +583,20 @@ impl SettingsStore { cx: &mut AppContext, ) -> Result<()> { // Reload the global and local values for every setting. - let mut user_settings_stack = Vec::::new(); + let mut project_settings_stack = Vec::::new(); let mut paths_stack = Vec::>::new(); for setting_value in self.setting_values.values_mut() { let default_settings = setting_value.deserialize_setting(&self.raw_default_settings)?; - user_settings_stack.clear(); - paths_stack.clear(); + let extension_settings = setting_value + .deserialize_setting(&self.raw_extension_settings) + .log_err(); - if let Some(user_settings) = setting_value + let user_settings = setting_value .deserialize_setting(&self.raw_user_settings) - .log_err() - { - user_settings_stack.push(user_settings); - paths_stack.push(None); - } + .log_err(); + let mut release_channel_settings = None; if let Some(release_settings) = &self .raw_user_settings .get(release_channel::RELEASE_CHANNEL.dev_name()) @@ -575,15 +605,25 @@ impl SettingsStore { .deserialize_setting(release_settings) .log_err() { - user_settings_stack.push(release_settings); - paths_stack.push(None); + release_channel_settings = Some(release_settings); } } // If the global settings file changed, reload the global value for the field. + project_settings_stack.clear(); + paths_stack.clear(); if changed_local_path.is_none() { if let Some(value) = setting_value - .load_setting(&default_settings, &user_settings_stack, cx) + .load_setting( + SettingsSources { + default: &default_settings, + extensions: extension_settings.as_ref(), + user: user_settings.as_ref(), + release_channel: release_channel_settings.as_ref(), + project: &[], + }, + cx, + ) .log_err() { setting_value.set_global_value(value); @@ -597,7 +637,7 @@ impl SettingsStore { if let Some((prev_root_id, prev_path)) = prev_entry { if root_id != prev_root_id || !path.starts_with(prev_path) { paths_stack.pop(); - user_settings_stack.pop(); + project_settings_stack.pop(); continue; } } @@ -608,7 +648,7 @@ impl SettingsStore { setting_value.deserialize_setting(local_settings).log_err() { paths_stack.push(Some((*root_id, path.as_ref()))); - user_settings_stack.push(local_settings); + project_settings_stack.push(local_settings); // If a local settings file changed, then avoid recomputing local // settings for any path outside of that directory. @@ -619,7 +659,16 @@ impl SettingsStore { } if let Some(value) = setting_value - .load_setting(&default_settings, &user_settings_stack, cx) + .load_setting( + SettingsSources { + default: &default_settings, + extensions: extension_settings.as_ref(), + user: user_settings.as_ref(), + release_channel: release_channel_settings.as_ref(), + project: &project_settings_stack.iter().collect::>(), + }, + cx, + ) .log_err() { setting_value.set_local_value(*root_id, path.clone(), value); @@ -660,16 +709,30 @@ impl AnySettingValue for SettingValue { fn load_setting( &self, - default_value: &DeserializedSetting, - user_values: &[DeserializedSetting], + values: SettingsSources, cx: &mut AppContext, ) -> Result> { - let default_value = default_value.0.downcast_ref::().unwrap(); - let values: SmallVec<[&T::FileContent; 6]> = user_values - .iter() - .map(|value| value.0.downcast_ref().unwrap()) - .collect(); - Ok(Box::new(T::load(default_value, &values, cx)?)) + Ok(Box::new(T::load( + SettingsSources { + default: values.default.0.downcast_ref::().unwrap(), + extensions: values + .extensions + .map(|value| value.0.downcast_ref::().unwrap()), + user: values + .user + .map(|value| value.0.downcast_ref::().unwrap()), + release_channel: values + .release_channel + .map(|value| value.0.downcast_ref::().unwrap()), + project: values + .project + .iter() + .map(|value| value.0.downcast_ref().unwrap()) + .collect::>() + .as_slice(), + }, + cx, + )?)) } fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result { @@ -1277,12 +1340,8 @@ mod tests { const KEY: Option<&'static str> = Some("user"); type FileContent = UserSettingsJson; - fn load( - default_value: &UserSettingsJson, - user_values: &[&UserSettingsJson], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } @@ -1293,12 +1352,8 @@ mod tests { const KEY: Option<&'static str> = Some("turbo"); type FileContent = Option; - fn load( - default_value: &Option, - user_values: &[&Option], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } @@ -1321,12 +1376,8 @@ mod tests { type FileContent = MultiKeySettingsJson; - fn load( - default_value: &MultiKeySettingsJson, - user_values: &[&MultiKeySettingsJson], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } @@ -1354,12 +1405,8 @@ mod tests { type FileContent = JournalSettingsJson; - fn load( - default_value: &JournalSettingsJson, - user_values: &[&JournalSettingsJson], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } @@ -1380,8 +1427,8 @@ mod tests { type FileContent = Self; - fn load(default_value: &Self, user_values: &[&Self], _: &mut AppContext) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } } diff --git a/crates/tasks_ui/src/settings.rs b/crates/tasks_ui/src/settings.rs index 8bff20a984..1bcd496264 100644 --- a/crates/tasks_ui/src/settings.rs +++ b/crates/tasks_ui/src/settings.rs @@ -1,5 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use settings::{Settings, SettingsSources}; #[derive(Serialize, Deserialize, PartialEq, Default)] pub(crate) struct TaskSettings { @@ -13,22 +14,15 @@ pub(crate) struct TaskSettingsContent { show_status_indicator: Option, } -impl settings::Settings for TaskSettings { +impl Settings for TaskSettings { const KEY: Option<&'static str> = Some("task"); type FileContent = TaskSettingsContent; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, - ) -> gpui::Result - where - Self: Sized, - { - let this = Self::json_merge(default_value, user_values)?; - Ok(Self { - show_status_indicator: this.show_status_indicator.unwrap_or(true), - }) + ) -> gpui::Result { + sources.json_merge() } } diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index 59b39751d1..863d8eff25 100644 --- a/crates/terminal/src/terminal_settings.rs +++ b/crates/terminal/src/terminal_settings.rs @@ -7,7 +7,7 @@ use schemars::{ }; use serde_derive::{Deserialize, Serialize}; use serde_json::Value; -use settings::SettingsJsonSchemaParams; +use settings::{SettingsJsonSchemaParams, SettingsSources}; use std::path::PathBuf; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -171,12 +171,12 @@ impl settings::Settings for TerminalSettings { type FileContent = TerminalSettingsContent; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } + fn json_schema( generator: &mut SchemaGenerator, params: &SettingsJsonSchemaParams, diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index f51944b86e..b7f08b2450 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -14,7 +14,7 @@ use schemars::{ }; use serde::{Deserialize, Serialize}; use serde_json::Value; -use settings::{Settings, SettingsJsonSchemaParams}; +use settings::{Settings, SettingsJsonSchemaParams, SettingsSources}; use std::sync::Arc; use util::ResultExt as _; @@ -316,14 +316,11 @@ impl settings::Settings for ThemeSettings { type FileContent = ThemeSettingsContent; - fn load( - defaults: &Self::FileContent, - user_values: &[&Self::FileContent], - cx: &mut AppContext, - ) -> Result { + fn load(sources: SettingsSources, cx: &mut AppContext) -> Result { let themes = ThemeRegistry::default_global(cx); let system_appearance = SystemAppearance::default_global(cx); + let defaults = sources.default; let mut this = Self { ui_font_size: defaults.ui_font_size.unwrap().into(), ui_font: Font { @@ -348,15 +345,15 @@ impl settings::Settings for ThemeSettings { theme_overrides: None, }; - for value in user_values.iter().copied().cloned() { - if let Some(value) = value.buffer_font_family { + for value in sources.user.into_iter().chain(sources.release_channel) { + if let Some(value) = value.buffer_font_family.clone() { this.buffer_font.family = value.into(); } if let Some(value) = value.buffer_font_features { this.buffer_font.features = value; } - if let Some(value) = value.ui_font_family { + if let Some(value) = value.ui_font_family.clone() { this.ui_font.family = value.into(); } if let Some(value) = value.ui_font_features { @@ -373,7 +370,7 @@ impl settings::Settings for ThemeSettings { } } - this.theme_overrides = value.theme_overrides; + this.theme_overrides = value.theme_overrides.clone(); this.apply_theme_overrides(); merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into)); diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 73bfd5fd3f..e8dc3bfe5b 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -35,7 +35,7 @@ use replace::multi_replace; use schemars::JsonSchema; use serde::Deserialize; use serde_derive::Serialize; -use settings::{update_settings_file, Settings, SettingsStore}; +use settings::{update_settings_file, Settings, SettingsSources, SettingsStore}; use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState}; use std::{ops::Range, sync::Arc}; use surrounds::{add_surrounds, change_surrounds, delete_surrounds}; @@ -779,13 +779,9 @@ impl Settings for VimModeSetting { type FileContent = Option; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { - Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or( - default_value.ok_or_else(Self::missing_default)?, + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + Ok(Self(sources.user.copied().flatten().unwrap_or( + sources.default.ok_or_else(Self::missing_default)?, ))) } } @@ -824,11 +820,7 @@ impl Settings for VimSettings { type FileContent = VimSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/welcome/src/base_keymap_setting.rs b/crates/welcome/src/base_keymap_setting.rs index d10e424eb6..dd837c39c7 100644 --- a/crates/welcome/src/base_keymap_setting.rs +++ b/crates/welcome/src/base_keymap_setting.rs @@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; /// Base key bindings scheme. Base keymaps can be overridden with user keymaps. /// @@ -70,16 +70,12 @@ impl Settings for BaseKeymap { type FileContent = Option; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut gpui::AppContext, - ) -> anyhow::Result - where - Self: Sized, - { - Ok(user_values - .first() - .and_then(|v| **v) - .unwrap_or(default_value.unwrap())) + ) -> anyhow::Result { + if let Some(Some(user_value)) = sources.user.copied() { + return Ok(user_value); + } + sources.default.ok_or_else(Self::missing_default) } } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 8cbe042cbc..9da551c4b6 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -20,7 +20,7 @@ use gpui::{ use project::{Project, ProjectEntryId, ProjectPath}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -76,12 +76,8 @@ impl Settings for ItemSettings { type FileContent = ItemSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut AppContext, - ) -> Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index a4841f0a34..f7317f639c 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -1,6 +1,8 @@ +use anyhow::Result; +use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Deserialize)] pub struct WorkspaceSettings { @@ -63,12 +65,8 @@ impl Settings for WorkspaceSettings { type FileContent = WorkspaceSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut gpui::AppContext, - ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } @@ -77,11 +75,7 @@ impl Settings for TabBarSettings { type FileContent = TabBarSettingsContent; - fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], - _: &mut gpui::AppContext, - ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + fn load(sources: SettingsSources, _: &mut AppContext) -> Result { + sources.json_merge() } } diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 432e274cf2..3941dc0ffb 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -1,7 +1,7 @@ use gpui::AppContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsSources}; #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct WorktreeSettings { @@ -31,10 +31,9 @@ impl Settings for WorktreeSettings { type FileContent = Self; fn load( - default_value: &Self::FileContent, - user_values: &[&Self::FileContent], + sources: SettingsSources, _: &mut AppContext, ) -> anyhow::Result { - Self::load_via_json_merge(default_value, user_values) + sources.json_merge() } } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a5607519a8..5824cb27d7 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -607,6 +607,7 @@ pub fn handle_keymap_file_changes( cx.observe_global::(move |cx| { let new_base_keymap = *BaseKeymap::get_global(cx); let new_vim_enabled = VimModeSetting::get_global(cx).0; + if new_base_keymap != old_base_keymap || new_vim_enabled != old_vim_enabled { old_base_keymap = new_base_keymap; old_vim_enabled = new_vim_enabled; @@ -3062,6 +3063,7 @@ mod tests { let mut app_state = AppState::test(cx); let state = Arc::get_mut(&mut app_state).unwrap(); + env_logger::try_init().ok(); state.build_window_options = build_window_options; theme::init(theme::LoadThemes::JustBase, cx); diff --git a/extensions/gleam/languages/gleam/config.toml b/extensions/gleam/languages/gleam/config.toml index 0a472172ad..51874945e2 100644 --- a/extensions/gleam/languages/gleam/config.toml +++ b/extensions/gleam/languages/gleam/config.toml @@ -9,3 +9,4 @@ brackets = [ { start = "(", end = ")", close = true, newline = true }, { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] }, ] +tab_size = 2 diff --git a/extensions/prisma/languages/prisma/config.toml b/extensions/prisma/languages/prisma/config.toml index c7f2d1ef6f..82fbb10458 100644 --- a/extensions/prisma/languages/prisma/config.toml +++ b/extensions/prisma/languages/prisma/config.toml @@ -7,3 +7,4 @@ brackets = [ { start = "[", end = "]", close = true, newline = true }, { start = "(", end = ")", close = true, newline = true } ] +tab_size = 2