TEMP

Conrad Irwin created

Change summary

crates/languages/src/lib.rs                     |   7 
crates/settings/src/base_keymap_setting.rs      |  48 
crates/settings/src/editable_setting_control.rs |  10 
crates/settings/src/settings.rs                 |   1 
crates/settings/src/settings_content.rs         | 972 +++++++++++++++++++
crates/settings/src/settings_file.rs            |   8 
crates/settings/src/settings_store.rs           | 840 ++++++---------
7 files changed, 1,362 insertions(+), 524 deletions(-)

Detailed changes

crates/languages/src/lib.rs 🔗

@@ -311,7 +311,12 @@ pub fn init(languages: Arc<LanguageRegistry>, fs: Arc<dyn Fs>, node: NodeRuntime
                 cx.update(|cx| {
                     SettingsStore::update_global(cx, |settings, cx| {
                         settings
-                            .set_extension_settings(language_settings.clone(), cx)
+                            .set_extension_settings(
+                                ExtensionsSettingsContent {
+                                    languages: language_settings.clone(),
+                                },
+                                cx,
+                            )
                             .log_err();
                     });
                 })?;

crates/settings/src/base_keymap_setting.rs 🔗

@@ -1,9 +1,12 @@
 use std::fmt::{Display, Formatter};
 
-use crate::{self as settings};
+use crate::{
+    self as settings,
+    settings_content::{self, BaseKeymapContent, SettingsContent},
+};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsSources, VsCodeSettings};
+use settings::{Settings, VsCodeSettings};
 use settings_ui_macros::{SettingsKey, SettingsUi};
 
 /// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
@@ -24,6 +27,21 @@ pub enum BaseKeymap {
     None,
 }
 
+impl From<BaseKeymapContent> for BaseKeymap {
+    fn from(value: BaseKeymapContent) -> Self {
+        match value {
+            BaseKeymapContent::VSCode => Self::VSCode,
+            BaseKeymapContent::JetBrains => Self::JetBrains,
+            BaseKeymapContent::SublimeText => Self::SublimeText,
+            BaseKeymapContent::Atom => Self::Atom,
+            BaseKeymapContent::TextMate => Self::TextMate,
+            BaseKeymapContent::Emacs => Self::Emacs,
+            BaseKeymapContent::Cursor => Self::Cursor,
+            BaseKeymapContent::None => Self::None,
+        }
+    }
+}
+
 impl Display for BaseKeymap {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
@@ -120,25 +138,17 @@ pub struct BaseKeymapSetting {
 }
 
 impl Settings for BaseKeymap {
-    type FileContent = BaseKeymapSetting;
+    fn from_file(s: &crate::settings_content::SettingsContent) -> Option<Self> {
+        s.base_keymap.map(Into::into)
+    }
 
-    fn load(
-        sources: SettingsSources<Self::FileContent>,
-        _: &mut gpui::App,
-    ) -> anyhow::Result<Self> {
-        if let Some(Some(user_value)) = sources.user.map(|setting| setting.base_keymap) {
-            return Ok(user_value);
-        }
-        if let Some(Some(server_value)) = sources.server.map(|setting| setting.base_keymap) {
-            return Ok(server_value);
-        }
-        sources
-            .default
-            .base_keymap
-            .ok_or_else(Self::missing_default)
+    fn refine(&mut self, s: &settings_content::SettingsContent) {
+        if let Some(base_keymap) = s.base_keymap {
+            *self = base_keymap.into();
+        };
     }
 
-    fn import_from_vscode(_vscode: &VsCodeSettings, current: &mut Self::FileContent) {
-        current.base_keymap = Some(BaseKeymap::VSCode);
+    fn import_from_vscode(_vscode: &VsCodeSettings, current: &mut SettingsContent) {
+        current.base_keymap = Some(BaseKeymapContent::VSCode);
     }
 }

crates/settings/src/editable_setting_control.rs 🔗

@@ -1,7 +1,7 @@
 use fs::Fs;
 use gpui::{App, RenderOnce, SharedString};
 
-use crate::{Settings, update_settings_file};
+use crate::{Settings, settings_content::SettingsContent, update_settings_file};
 
 /// A UI control that can be used to edit a setting.
 pub trait EditableSettingControl: RenderOnce {
@@ -20,17 +20,13 @@ pub trait EditableSettingControl: RenderOnce {
     /// Applies the given setting file to the settings file contents.
     ///
     /// This will be called when writing the setting value back to the settings file.
-    fn apply(
-        settings: &mut <Self::Settings as Settings>::FileContent,
-        value: Self::Value,
-        cx: &App,
-    );
+    fn apply(settings: &mut SettingsContent, value: Self::Value, cx: &App);
 
     /// Writes the given setting value to the settings files.
     fn write(value: Self::Value, cx: &App) {
         let fs = <dyn Fs>::global(cx);
 
-        update_settings_file::<Self::Settings>(fs, cx, move |settings, cx| {
+        update_settings_file(fs, cx, move |settings, cx| {
             Self::apply(settings, value, cx);
         });
     }

crates/settings/src/settings.rs 🔗

@@ -1,6 +1,7 @@
 mod base_keymap_setting;
 mod editable_setting_control;
 mod keymap_file;
+mod settings_content;
 mod settings_file;
 mod settings_json;
 mod settings_store;

crates/settings/src/settings_content.rs 🔗

@@ -0,0 +1,972 @@
+use std::borrow::Cow;
+use std::env;
+use std::num::NonZeroU32;
+use std::sync::Arc;
+
+use collections::{HashMap, HashSet};
+use gpui::{App, Modifiers, SharedString};
+use release_channel::ReleaseChannel;
+use schemars::{JsonSchema, json_schema};
+use serde::de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor};
+use serde::{Deserialize, Deserializer, Serialize};
+use util::schemars::replace_subschema;
+use util::serde::default_true;
+
+use crate::{ActiveSettingsProfileName, ParameterizedJsonSchema, Settings};
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
+pub struct SettingsContent {
+    #[serde(flatten)]
+    pub project: ProjectSettingsContent,
+
+    pub base_keymap: Option<BaseKeymapContent>,
+}
+
+impl SettingsContent {
+    pub fn languages_mut(&mut self) -> &mut HashMap<SharedString, LanguageSettingsContent> {
+        &mut self.project.all_languages.languages.0
+    }
+}
+
+// todo!() what should this be?
+#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
+pub struct ServerSettingsContent {
+    #[serde(flatten)]
+    project: ProjectSettingsContent,
+}
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
+pub(crate) struct UserSettingsContent {
+    #[serde(flatten)]
+    pub(crate) content: SettingsContent,
+
+    pub(crate) dev: Option<SettingsContent>,
+    pub(crate) nightly: Option<SettingsContent>,
+    pub(crate) preview: Option<SettingsContent>,
+    pub(crate) stable: Option<SettingsContent>,
+
+    pub(crate) macos: Option<SettingsContent>,
+    pub(crate) windows: Option<SettingsContent>,
+    pub(crate) linux: Option<SettingsContent>,
+
+    #[serde(default)]
+    pub(crate) profiles: HashMap<String, SettingsContent>,
+}
+
+pub struct ExtensionsSettingsContent {
+    pub(crate) all_languages: AllLanguageSettingsContent,
+}
+
+impl UserSettingsContent {
+    pub(crate) fn for_release_channel(&self) -> Option<&SettingsContent> {
+        match *release_channel::RELEASE_CHANNEL {
+            ReleaseChannel::Dev => self.dev.as_ref(),
+            ReleaseChannel::Nightly => self.nightly.as_ref(),
+            ReleaseChannel::Preview => self.preview.as_ref(),
+            ReleaseChannel::Stable => self.stable.as_ref(),
+        }
+    }
+
+    pub(crate) fn for_os(&self) -> Option<&SettingsContent> {
+        match env::consts::OS {
+            "macos" => self.macos.as_ref(),
+            "linux" => self.linux.as_ref(),
+            "windows" => self.windows.as_ref(),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn for_profile(&self, cx: &App) -> Option<&SettingsContent> {
+        let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>() else {
+            return None;
+        };
+        self.profiles.get(&active_profile.0)
+    }
+}
+
+/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
+///
+/// Default: VSCode
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
+pub enum BaseKeymapContent {
+    #[default]
+    VSCode,
+    JetBrains,
+    SublimeText,
+    Atom,
+    TextMate,
+    Emacs,
+    Cursor,
+    None,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+pub struct ProjectSettingsContent {
+    #[serde(flatten)]
+    pub(crate) all_languages: AllLanguageSettingsContent,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+pub struct AllLanguageSettingsContent {
+    /// The settings for enabling/disabling features.
+    #[serde(default)]
+    pub features: Option<FeaturesContent>,
+    /// The edit prediction settings.
+    #[serde(default)]
+    pub edit_predictions: Option<EditPredictionSettingsContent>,
+    /// The default language settings.
+    #[serde(flatten)]
+    pub defaults: LanguageSettingsContent,
+    /// The settings for individual languages.
+    #[serde(default)]
+    pub languages: LanguageToSettingsMap,
+    /// Settings for associating file extensions and filenames
+    /// with languages.
+    #[serde(default)]
+    pub file_types: HashMap<SharedString, Vec<String>>,
+}
+
+/// The settings for enabling/disabling features.
+#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct FeaturesContent {
+    /// Determines which edit prediction provider to use.
+    pub edit_prediction_provider: Option<EditPredictionProviderContent>,
+}
+
+/// The provider that supplies edit predictions.
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum EditPredictionProviderContent {
+    None,
+    #[default]
+    Copilot,
+    Supermaven,
+    Zed,
+}
+
+/// The contents of the edit prediction settings.
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub struct EditPredictionSettingsContent {
+    /// A list of globs representing files that edit predictions should be disabled for.
+    /// This list adds to a pre-existing, sensible default set of globs.
+    /// Any additional ones you add are combined with them.
+    #[serde(default)]
+    pub disabled_globs: Option<Vec<String>>,
+    /// The mode used to display edit predictions in the buffer.
+    /// Provider support required.
+    #[serde(default)]
+    pub mode: EditPredictionsModeContent,
+    /// Settings specific to GitHub Copilot.
+    #[serde(default)]
+    pub copilot: CopilotSettingsContent,
+    /// Whether edit predictions are enabled in the assistant prompt editor.
+    /// This has no effect if globally disabled.
+    #[serde(default = "default_true")]
+    pub enabled_in_text_threads: bool,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub struct CopilotSettingsContent {
+    /// HTTP/HTTPS proxy to use for Copilot.
+    ///
+    /// Default: none
+    #[serde(default)]
+    pub proxy: Option<String>,
+    /// Disable certificate verification for the proxy (not recommended).
+    ///
+    /// Default: false
+    #[serde(default)]
+    pub proxy_no_verify: Option<bool>,
+    /// Enterprise URI for Copilot.
+    ///
+    /// Default: none
+    #[serde(default)]
+    pub enterprise_uri: Option<String>,
+}
+
+/// The mode in which edit predictions should be displayed.
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum EditPredictionsModeContent {
+    /// If provider supports it, display inline when holding modifier key (e.g., alt).
+    /// Otherwise, eager preview is used.
+    #[serde(alias = "auto")]
+    Subtle,
+    /// Display inline when there are no language server completions available.
+    #[default]
+    #[serde(alias = "eager_preview")]
+    Eager,
+}
+
+/// Controls the soft-wrapping behavior in the editor.
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SoftWrapContent {
+    /// Prefer a single line generally, unless an overly long line is encountered.
+    None,
+    /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
+    /// Prefer a single line generally, unless an overly long line is encountered.
+    PreferLine,
+    /// Soft wrap lines that exceed the editor width.
+    EditorWidth,
+    /// Soft wrap lines at the preferred line length.
+    PreferredLineLength,
+    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
+    Bounded,
+}
+
+/// The settings for a particular language.
+#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+pub struct LanguageSettingsContent {
+    /// How many columns a tab should occupy.
+    ///
+    /// Default: 4
+    #[serde(default)]
+    pub tab_size: Option<NonZeroU32>,
+    /// Whether to indent lines using tab characters, as opposed to multiple
+    /// spaces.
+    ///
+    /// Default: false
+    #[serde(default)]
+    pub hard_tabs: Option<bool>,
+    /// How to soft-wrap long lines of text.
+    ///
+    /// Default: none
+    #[serde(default)]
+    pub soft_wrap: Option<SoftWrapContent>,
+    /// The column at which to soft-wrap lines, for buffers where soft-wrap
+    /// is enabled.
+    ///
+    /// Default: 80
+    #[serde(default)]
+    pub preferred_line_length: Option<u32>,
+    /// Whether to show wrap guides in the editor. Setting this to true will
+    /// show a guide at the 'preferred_line_length' value if softwrap is set to
+    /// 'preferred_line_length', and will show any additional guides as specified
+    /// by the 'wrap_guides' setting.
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub show_wrap_guides: Option<bool>,
+    /// Character counts at which to show wrap guides in the editor.
+    ///
+    /// Default: []
+    #[serde(default)]
+    pub wrap_guides: Option<Vec<usize>>,
+    /// Indent guide related settings.
+    #[serde(default)]
+    pub indent_guides: Option<IndentGuideSettingsContent>,
+    /// Whether or not to perform a buffer format before saving.
+    ///
+    /// Default: on
+    #[serde(default)]
+    pub format_on_save: Option<FormatOnSave>,
+    /// Whether or not to remove any trailing whitespace from lines of a buffer
+    /// before saving it.
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub remove_trailing_whitespace_on_save: Option<bool>,
+    /// Whether or not to ensure there's a single newline at the end of a buffer
+    /// when saving it.
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub ensure_final_newline_on_save: Option<bool>,
+    /// How to perform a buffer format.
+    ///
+    /// Default: auto
+    #[serde(default)]
+    pub formatter: Option<SelectedFormatter>,
+    /// Zed's Prettier integration settings.
+    /// Allows to enable/disable formatting with Prettier
+    /// and configure default Prettier, used when no project-level Prettier installation is found.
+    ///
+    /// Default: off
+    #[serde(default)]
+    pub prettier: Option<PrettierSettings>,
+    /// Whether to automatically close JSX tags.
+    #[serde(default)]
+    pub jsx_tag_auto_close: Option<JsxTagAutoCloseSettings>,
+    /// Whether to use language servers to provide code intelligence.
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub enable_language_server: Option<bool>,
+    /// The list of language servers to use (or disable) for this language.
+    ///
+    /// This array should consist of language server IDs, as well as the following
+    /// special tokens:
+    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
+    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
+    ///
+    /// Default: ["..."]
+    #[serde(default)]
+    pub language_servers: Option<Vec<String>>,
+    /// Controls where the `editor::Rewrap` action is allowed for this language.
+    ///
+    /// Note: This setting has no effect in Vim mode, as rewrap is already
+    /// allowed everywhere.
+    ///
+    /// Default: "in_comments"
+    #[serde(default)]
+    pub allow_rewrap: Option<RewrapBehavior>,
+    /// Controls whether edit predictions are shown immediately (true)
+    /// or manually by triggering `editor::ShowEditPrediction` (false).
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub show_edit_predictions: Option<bool>,
+    /// Controls whether edit predictions are shown in the given language
+    /// scopes.
+    ///
+    /// Example: ["string", "comment"]
+    ///
+    /// Default: []
+    #[serde(default)]
+    pub edit_predictions_disabled_in: Option<Vec<String>>,
+    /// Whether to show tabs and spaces in the editor.
+    #[serde(default)]
+    pub show_whitespaces: Option<ShowWhitespaceSetting>,
+    /// Visible characters used to render whitespace when show_whitespaces is enabled.
+    ///
+    /// Default: "•" for spaces, "→" for tabs.
+    #[serde(default)]
+    pub whitespace_map: Option<WhitespaceMap>,
+    /// Whether to start a new line with a comment when a previous line is a comment as well.
+    ///
+    /// Default: true
+    #[serde(default)]
+    pub extend_comment_on_newline: Option<bool>,
+    /// Inlay hint related settings.
+    #[serde(default)]
+    pub inlay_hints: Option<InlayHintSettings>,
+    /// Whether to automatically type closing characters for you. For example,
+    /// when you type (, Zed will automatically add a closing ) at the correct position.
+    ///
+    /// Default: true
+    pub use_autoclose: Option<bool>,
+    /// Whether to automatically surround text with characters for you. For example,
+    /// when you select text and type (, Zed will automatically surround text with ().
+    ///
+    /// Default: true
+    pub use_auto_surround: Option<bool>,
+    /// Controls how the editor handles the autoclosed characters.
+    /// When set to `false`(default), skipping over and auto-removing of the closing characters
+    /// happen only for auto-inserted characters.
+    /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
+    /// no matter how they were inserted.
+    ///
+    /// Default: false
+    pub always_treat_brackets_as_autoclosed: Option<bool>,
+    /// Whether to use additional LSP queries to format (and amend) the code after
+    /// every "trigger" symbol input, defined by LSP server capabilities.
+    ///
+    /// Default: true
+    pub use_on_type_format: Option<bool>,
+    /// Which code actions to run on save after the formatter.
+    /// These are not run if formatting is off.
+    ///
+    /// Default: {} (or {"source.organizeImports": true} for Go).
+    pub code_actions_on_format: Option<HashMap<String, bool>>,
+    /// Whether to perform linked edits of associated ranges, if the language server supports it.
+    /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
+    ///
+    /// Default: true
+    pub linked_edits: Option<bool>,
+    /// Whether indentation should be adjusted based on the context whilst typing.
+    ///
+    /// Default: true
+    pub auto_indent: Option<bool>,
+    /// Whether indentation of pasted content should be adjusted based on the context.
+    ///
+    /// Default: true
+    pub auto_indent_on_paste: Option<bool>,
+    /// Task configuration for this language.
+    ///
+    /// Default: {}
+    pub tasks: Option<LanguageTaskConfig>,
+    /// Whether to pop the completions menu while typing in an editor without
+    /// explicitly requesting it.
+    ///
+    /// Default: true
+    pub show_completions_on_input: Option<bool>,
+    /// Whether to display inline and alongside documentation for items in the
+    /// completions menu.
+    ///
+    /// Default: true
+    pub show_completion_documentation: Option<bool>,
+    /// Controls how completions are processed for this language.
+    pub completions: Option<CompletionSettings>,
+    /// Preferred debuggers for this language.
+    ///
+    /// Default: []
+    pub debuggers: Option<Vec<String>>,
+}
+
+/// Controls how whitespace should be displayedin the editor.
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ShowWhitespaceSetting {
+    /// Draw whitespace only for the selected text.
+    Selection,
+    /// Do not draw any tabs or spaces.
+    None,
+    /// Draw all invisible symbols.
+    All,
+    /// Draw whitespaces at boundaries only.
+    ///
+    /// For a whitespace to be on a boundary, any of the following conditions need to be met:
+    /// - It is a tab
+    /// - It is adjacent to an edge (start or end)
+    /// - It is adjacent to a whitespace (left or right)
+    Boundary,
+    /// Draw whitespaces only after non-whitespace characters.
+    Trailing,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub struct WhitespaceMap {
+    #[serde(default)]
+    pub space: Option<String>,
+    #[serde(default)]
+    pub tab: Option<String>,
+}
+
+impl WhitespaceMap {
+    pub fn space(&self) -> SharedString {
+        self.space
+            .as_ref()
+            .map_or_else(|| SharedString::from("•"), |s| SharedString::from(s))
+    }
+
+    pub fn tab(&self) -> SharedString {
+        self.tab
+            .as_ref()
+            .map_or_else(|| SharedString::from("→"), |s| SharedString::from(s))
+    }
+}
+
+/// The behavior of `editor::Rewrap`.
+#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum RewrapBehavior {
+    /// Only rewrap within comments.
+    #[default]
+    InComments,
+    /// Only rewrap within the current selection(s).
+    InSelections,
+    /// Allow rewrapping anywhere.
+    Anywhere,
+}
+
+#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+pub struct JsxTagAutoCloseSettings {
+    /// Enables or disables auto-closing of JSX tags.
+    #[serde(default)]
+    pub enabled: bool,
+}
+
+/// The settings for inlay hints.
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+pub struct InlayHintSettings {
+    /// Global switch to toggle hints on and off.
+    ///
+    /// Default: false
+    #[serde(default)]
+    pub enabled: bool,
+    /// Global switch to toggle inline values on and off when debugging.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub show_value_hints: bool,
+    /// Whether type hints should be shown.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub show_type_hints: bool,
+    /// Whether parameter hints should be shown.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub show_parameter_hints: bool,
+    /// Whether other hints should be shown.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub show_other_hints: bool,
+    /// Whether to show a background for inlay hints.
+    ///
+    /// If set to `true`, the background will use the `hint.background` color
+    /// from the current theme.
+    ///
+    /// Default: false
+    #[serde(default)]
+    pub show_background: bool,
+    /// Whether or not to debounce inlay hints updates after buffer edits.
+    ///
+    /// Set to 0 to disable debouncing.
+    ///
+    /// Default: 700
+    #[serde(default = "edit_debounce_ms")]
+    pub edit_debounce_ms: u64,
+    /// Whether or not to debounce inlay hints updates after buffer scrolls.
+    ///
+    /// Set to 0 to disable debouncing.
+    ///
+    /// Default: 50
+    #[serde(default = "scroll_debounce_ms")]
+    pub scroll_debounce_ms: u64,
+    /// Toggles inlay hints (hides or shows) when the user presses the modifiers specified.
+    /// If only a subset of the modifiers specified is pressed, hints are not toggled.
+    /// If no modifiers are specified, this is equivalent to `None`.
+    ///
+    /// Default: None
+    #[serde(default)]
+    pub toggle_on_modifiers_press: Option<Modifiers>,
+}
+
+fn edit_debounce_ms() -> u64 {
+    700
+}
+
+fn scroll_debounce_ms() -> u64 {
+    50
+}
+
+/// Controls how completions are processed for this language.
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct CompletionSettings {
+    /// Controls how words are completed.
+    /// For large documents, not all words may be fetched for completion.
+    ///
+    /// Default: `fallback`
+    #[serde(default = "default_words_completion_mode")]
+    pub words: WordsCompletionMode,
+    /// How many characters has to be in the completions query to automatically show the words-based completions.
+    /// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
+    ///
+    /// Default: 3
+    #[serde(default = "default_3")]
+    pub words_min_length: usize,
+    /// Whether to fetch LSP completions or not.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub lsp: bool,
+    /// When fetching LSP completions, determines how long to wait for a response of a particular server.
+    /// When set to 0, waits indefinitely.
+    ///
+    /// Default: 0
+    #[serde(default)]
+    pub lsp_fetch_timeout_ms: u64,
+    /// Controls how LSP completions are inserted.
+    ///
+    /// Default: "replace_suffix"
+    #[serde(default = "default_lsp_insert_mode")]
+    pub lsp_insert_mode: LspInsertMode,
+}
+
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum LspInsertMode {
+    /// Replaces text before the cursor, using the `insert` range described in the LSP specification.
+    Insert,
+    /// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
+    Replace,
+    /// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
+    /// and like `"insert"` otherwise.
+    ReplaceSubsequence,
+    /// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
+    /// `"insert"` otherwise.
+    ReplaceSuffix,
+}
+
+/// Controls how document's words are completed.
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum WordsCompletionMode {
+    /// Always fetch document's words for completions along with LSP completions.
+    Enabled,
+    /// Only if LSP response errors or times out,
+    /// use document's words to show completions.
+    Fallback,
+    /// Never fetch or complete document's words for completions.
+    /// (Word-based completions can still be queried via a separate action)
+    Disabled,
+}
+
+fn default_words_completion_mode() -> WordsCompletionMode {
+    WordsCompletionMode::Fallback
+}
+
+fn default_lsp_insert_mode() -> LspInsertMode {
+    LspInsertMode::ReplaceSuffix
+}
+
+fn default_3() -> usize {
+    3
+}
+
+/// Allows to enable/disable formatting with Prettier
+/// and configure default Prettier, used when no project-level Prettier installation is found.
+/// Prettier formatting is disabled by default.
+#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+pub struct PrettierSettings {
+    /// Enables or disables formatting with Prettier for a given language.
+    #[serde(default)]
+    pub allowed: bool,
+
+    /// Forces Prettier integration to use a specific parser name when formatting files with the language.
+    #[serde(default)]
+    pub parser: Option<String>,
+
+    /// Forces Prettier integration to use specific plugins when formatting files with the language.
+    /// The default Prettier will be installed with these plugins.
+    #[serde(default)]
+    pub plugins: HashSet<String>,
+
+    /// Default Prettier options, in the format as in package.json section for Prettier.
+    /// If project installs Prettier via its package.json, these options will be ignored.
+    #[serde(flatten)]
+    pub options: HashMap<String, serde_json::Value>,
+}
+/// Controls the behavior of formatting files when they are saved.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FormatOnSave {
+    /// Files should be formatted on save.
+    On,
+    /// Files should not be formatted on save.
+    Off,
+    List(FormatterList),
+}
+
+impl JsonSchema for FormatOnSave {
+    fn schema_name() -> Cow<'static, str> {
+        "OnSaveFormatter".into()
+    }
+
+    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+        let formatter_schema = Formatter::json_schema(generator);
+
+        json_schema!({
+            "oneOf": [
+                {
+                    "type": "array",
+                    "items": formatter_schema
+                },
+                {
+                    "type": "string",
+                    "enum": ["on", "off", "language_server"]
+                },
+                formatter_schema
+            ]
+        })
+    }
+}
+
+impl Serialize for FormatOnSave {
+    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        match self {
+            Self::On => serializer.serialize_str("on"),
+            Self::Off => serializer.serialize_str("off"),
+            Self::List(list) => list.serialize(serializer),
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for FormatOnSave {
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct FormatDeserializer;
+
+        impl<'d> Visitor<'d> for FormatDeserializer {
+            type Value = FormatOnSave;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("a valid on-save formatter kind")
+            }
+            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                if v == "on" {
+                    Ok(Self::Value::On)
+                } else if v == "off" {
+                    Ok(Self::Value::Off)
+                } else if v == "language_server" {
+                    Ok(Self::Value::List(FormatterList::Single(
+                        Formatter::LanguageServer { name: None },
+                    )))
+                } else {
+                    let ret: Result<FormatterList, _> =
+                        Deserialize::deserialize(v.into_deserializer());
+                    ret.map(Self::Value::List)
+                }
+            }
+            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+            where
+                A: MapAccess<'d>,
+            {
+                let ret: Result<FormatterList, _> =
+                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
+                ret.map(Self::Value::List)
+            }
+            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
+            where
+                A: SeqAccess<'d>,
+            {
+                let ret: Result<FormatterList, _> =
+                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
+                ret.map(Self::Value::List)
+            }
+        }
+        deserializer.deserialize_any(FormatDeserializer)
+    }
+}
+
+/// Controls which formatter should be used when formatting code.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub enum SelectedFormatter {
+    /// Format files using Zed's Prettier integration (if applicable),
+    /// or falling back to formatting via language server.
+    #[default]
+    Auto,
+    List(FormatterList),
+}
+
+impl JsonSchema for SelectedFormatter {
+    fn schema_name() -> Cow<'static, str> {
+        "Formatter".into()
+    }
+
+    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+        let formatter_schema = Formatter::json_schema(generator);
+
+        json_schema!({
+            "oneOf": [
+                {
+                    "type": "array",
+                    "items": formatter_schema
+                },
+                {
+                    "type": "string",
+                    "enum": ["auto", "language_server"]
+                },
+                formatter_schema
+            ]
+        })
+    }
+}
+
+impl Serialize for SelectedFormatter {
+    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        match self {
+            SelectedFormatter::Auto => serializer.serialize_str("auto"),
+            SelectedFormatter::List(list) => list.serialize(serializer),
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for SelectedFormatter {
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct FormatDeserializer;
+
+        impl<'d> Visitor<'d> for FormatDeserializer {
+            type Value = SelectedFormatter;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("a valid formatter kind")
+            }
+            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                if v == "auto" {
+                    Ok(Self::Value::Auto)
+                } else if v == "language_server" {
+                    Ok(Self::Value::List(FormatterList::Single(
+                        Formatter::LanguageServer { name: None },
+                    )))
+                } else {
+                    let ret: Result<FormatterList, _> =
+                        Deserialize::deserialize(v.into_deserializer());
+                    ret.map(SelectedFormatter::List)
+                }
+            }
+            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+            where
+                A: MapAccess<'d>,
+            {
+                let ret: Result<FormatterList, _> =
+                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
+                ret.map(SelectedFormatter::List)
+            }
+            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
+            where
+                A: SeqAccess<'d>,
+            {
+                let ret: Result<FormatterList, _> =
+                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
+                ret.map(SelectedFormatter::List)
+            }
+        }
+        deserializer.deserialize_any(FormatDeserializer)
+    }
+}
+
+/// Controls which formatters should be used when formatting code.
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(untagged)]
+pub enum FormatterList {
+    Single(Formatter),
+    Vec(Vec<Formatter>),
+}
+
+impl Default for FormatterList {
+    fn default() -> Self {
+        Self::Single(Formatter::default())
+    }
+}
+
+/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
+#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum Formatter {
+    /// Format code using the current language server.
+    LanguageServer { name: Option<String> },
+    /// Format code using Zed's Prettier integration.
+    #[default]
+    Prettier,
+    /// Format code using an external command.
+    External {
+        /// The external program to run.
+        command: Arc<str>,
+        /// The arguments to pass to the program.
+        arguments: Option<Arc<[String]>>,
+    },
+    /// Files should be formatted using code actions executed by language servers.
+    CodeActions(HashMap<String, bool>),
+}
+
+/// The settings for indent guides.
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+pub struct IndentGuideSettingsContent {
+    /// Whether to display indent guides in the editor.
+    ///
+    /// Default: true
+    #[serde(default = "default_true")]
+    pub enabled: bool,
+    /// The width of the indent guides in pixels, between 1 and 10.
+    ///
+    /// Default: 1
+    #[serde(default = "line_width")]
+    pub line_width: u32,
+    /// The width of the active indent guide in pixels, between 1 and 10.
+    ///
+    /// Default: 1
+    #[serde(default = "active_line_width")]
+    pub active_line_width: u32,
+    /// Determines how indent guides are colored.
+    ///
+    /// Default: Fixed
+    #[serde(default)]
+    pub coloring: IndentGuideColoring,
+    /// Determines how indent guide backgrounds are colored.
+    ///
+    /// Default: Disabled
+    #[serde(default)]
+    pub background_coloring: IndentGuideBackgroundColoring,
+}
+
+fn line_width() -> u32 {
+    1
+}
+
+fn active_line_width() -> u32 {
+    line_width()
+}
+
+/// The task settings for a particular language.
+#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
+pub struct LanguageTaskConfig {
+    /// Extra task variables to set for a particular language.
+    #[serde(default)]
+    pub variables: HashMap<String, String>,
+    #[serde(default = "default_true")]
+    pub enabled: bool,
+    /// Use LSP tasks over Zed language extension ones.
+    /// If no LSP tasks are returned due to error/timeout or regular execution,
+    /// Zed language extension tasks will be used instead.
+    ///
+    /// Other Zed tasks will still be shown:
+    /// * Zed task from either of the task config file
+    /// * Zed task from history (e.g. one-off task was spawned before)
+    #[serde(default = "default_true")]
+    pub prefer_lsp: bool,
+}
+
+/// Map from language name to settings. Its `ParameterizedJsonSchema` allows only known language
+/// names in the keys.
+#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
+
+inventory::submit! {
+    ParameterizedJsonSchema {
+        add_and_get_ref: |generator, params, _cx| {
+            let language_settings_content_ref = generator
+                .subschema_for::<LanguageSettingsContent>()
+                .to_value();
+            replace_subschema::<LanguageToSettingsMap>(generator, || json_schema!({
+                "type": "object",
+                "properties": params
+                    .language_names
+                    .iter()
+                    .map(|name| {
+                        (
+                            name.clone(),
+                            language_settings_content_ref.clone(),
+                        )
+                    })
+                    .collect::<serde_json::Map<_, _>>()
+            }))
+        }
+    }
+}
+
+/// Determines how indent guides are colored.
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum IndentGuideColoring {
+    /// Do not render any lines for indent guides.
+    Disabled,
+    /// Use the same color for all indentation levels.
+    #[default]
+    Fixed,
+    /// Use a different color for each indentation level.
+    IndentAware,
+}
+
+/// Determines how indent guide backgrounds are colored.
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum IndentGuideBackgroundColoring {
+    /// Do not render any background for indent guides.
+    #[default]
+    Disabled,
+    /// Use a different color for each indentation level.
+    IndentAware,
+}

crates/settings/src/settings_file.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Settings, settings_store::SettingsStore};
+use crate::{Settings, settings_content::SettingsContent, settings_store::SettingsStore};
 use collections::HashSet;
 use fs::{Fs, PathEventKind};
 use futures::{StreamExt, channel::mpsc};
@@ -126,10 +126,10 @@ pub fn watch_config_dir(
     rx
 }
 
-pub fn update_settings_file<T: Settings>(
+pub fn update_settings_file(
     fs: Arc<dyn Fs>,
     cx: &App,
-    update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
+    update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
 ) {
-    SettingsStore::global(cx).update_settings_file::<T>(fs, update);
+    SettingsStore::global(cx).update_settings_file(fs, update);
 }

crates/settings/src/settings_store.rs 🔗

@@ -33,7 +33,11 @@ pub type EditorconfigProperties = ec4rs::Properties;
 use crate::{
     ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUiEntry,
     VsCodeSettings, WorktreeId, parse_json_with_comments, replace_value_in_json_text,
-    settings_ui_core::SettingsUi, update_value_in_json_text,
+    settings_content::{
+        ExtensionsSettingsContent, ProjectSettingsContent, ServerSettingsContent, SettingsContent,
+        UserSettingsContent,
+    },
+    update_value_in_json_text,
 };
 
 pub trait SettingsKey: 'static + Send + Sync {
@@ -48,7 +52,7 @@ pub trait SettingsKey: 'static + Send + Sync {
 /// A value that can be defined as a user setting.
 ///
 /// Settings can be loaded from a combination of multiple JSON files.
-pub trait Settings: 'static + Send + Sync {
+pub trait Settings: 'static + Send + Sync + Sized {
     /// The name of the keys in the [`FileContent`](Self::FileContent) that should
     /// always be written to a settings file, even if their value matches the default
     /// value.
@@ -58,30 +62,9 @@ pub trait Settings: 'static + Send + Sync {
     /// user settings match the current version of the settings.
     const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
 
-    /// The type that is stored in an individual JSON file.
-    type FileContent: Clone
-        + Default
-        + Serialize
-        + DeserializeOwned
-        + JsonSchema
-        + SettingsUi
-        + SettingsKey;
-
-    /*
-     *  let path = Settings
-     *
-     *
-     */
-    /// The logic for combining together values from one or more JSON files into the
-    /// final value for this setting.
-    ///
-    /// # Warning
-    /// `Self::FileContent` deserialized field names should match with `Self` deserialized field names
-    /// otherwise the field won't be deserialized properly and you will get the error:
-    /// "A default setting must be added to the `default.json` file"
-    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self>
-    where
-        Self: Sized;
+    fn from_file(content: &SettingsContent) -> Option<Self>;
+
+    fn refine(&mut self, content: &SettingsContent);
 
     fn missing_default() -> anyhow::Error {
         anyhow::anyhow!("missing default for: {}", std::any::type_name::<Self>())
@@ -89,7 +72,7 @@ pub trait Settings: 'static + Send + Sync {
 
     /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known
     /// equivalent settings from a vscode config to our config
-    fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent);
+    fn import_from_vscode(vscode: &VsCodeSettings, current: &mut SettingsContent);
 
     #[track_caller]
     fn register(cx: &mut App)
@@ -218,17 +201,15 @@ pub struct SettingsLocation<'a> {
 /// A set of strongly-typed setting values defined via multiple config files.
 pub struct SettingsStore {
     setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
-    raw_default_settings: Value,
-    raw_global_settings: Option<Value>,
-    raw_user_settings: Value,
-    raw_server_settings: Option<Value>,
-    raw_extension_settings: Value,
-    raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), Value>,
+    default_settings: Option<SettingsContent>,
+    user_settings: Option<UserSettingsContent>,
+    global_settings: Option<SettingsContent>,
+
+    extension_settings: Option<SettingsContent>,
+    server_settings: Option<SettingsContent>,
+    local_settings: BTreeMap<(WorktreeId, Arc<Path>), SettingsContent>,
     raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>,
-    tab_size_callback: Option<(
-        TypeId,
-        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,
-    )>,
+
     _setting_file_updates: Task<()>,
     setting_file_updates_tx:
         mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,
@@ -271,20 +252,11 @@ struct SettingValue<T> {
 }
 
 trait AnySettingValue: 'static + Send + Sync {
-    fn key(&self) -> Option<&'static str>;
     fn setting_type_name(&self) -> &'static str;
-    fn deserialize_setting(&self, json: &Value) -> Result<DeserializedSetting> {
-        self.deserialize_setting_with_key(json).1
-    }
-    fn deserialize_setting_with_key(
-        &self,
-        json: &Value,
-    ) -> (Option<&'static str>, Result<DeserializedSetting>);
-    fn load_setting(
-        &self,
-        sources: SettingsSources<DeserializedSetting>,
-        cx: &mut App,
-    ) -> Result<Box<dyn Any>>;
+
+    fn from_file(&self, s: &SettingsContent) -> Option<Box<dyn Any>>;
+    fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent]);
+
     fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
     fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>;
     fn set_global_value(&mut self, value: Box<dyn Any>);
@@ -308,14 +280,13 @@ impl SettingsStore {
         let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
         Self {
             setting_values: Default::default(),
-            raw_default_settings: json!({}),
-            raw_global_settings: None,
-            raw_user_settings: json!({}),
-            raw_server_settings: None,
-            raw_extension_settings: json!({}),
-            raw_local_settings: Default::default(),
+            default_settings: Some(Default::default()), // todo!()
+            global_settings: None,
+            server_settings: None,
+            user_settings: Some(Default::default()), // todo!()
+            extension_settings: None,
+            local_settings: BTreeMap::default(),
             raw_editorconfig_settings: BTreeMap::default(),
-            tab_size_callback: Default::default(),
             setting_file_updates_tx,
             _setting_file_updates: cx.spawn(async move |cx| {
                 while let Some(setting_file_update) = setting_file_updates_rx.next().await {
@@ -354,71 +325,39 @@ impl SettingsStore {
             local_values: Vec::new(),
         }));
 
-        if let Some(default_settings) = setting_value
-            .deserialize_setting(&self.raw_default_settings)
-            .log_err()
-        {
-            let user_value = setting_value
-                .deserialize_setting(&self.raw_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())
-            {
-                release_channel_value = setting_value
-                    .deserialize_setting(release_settings)
-                    .log_err();
-            }
+        let mut refinements = Vec::default();
 
-            let mut os_settings_value = None;
-            if let Some(os_settings) = &self.raw_user_settings.get(env::consts::OS) {
-                os_settings_value = setting_value.deserialize_setting(os_settings).log_err();
-            }
+        if let Some(extension_settings) = self.extension_settings.as_ref() {
+            refinements.push(extension_settings)
+        }
 
-            let mut profile_value = None;
-            if let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>()
-                && let Some(profiles) = self.raw_user_settings.get("profiles")
-                && let Some(profile_settings) = profiles.get(&active_profile.0)
-            {
-                profile_value = setting_value
-                    .deserialize_setting(profile_settings)
-                    .log_err();
+        if let Some(user_settings) = self.user_settings.as_ref() {
+            refinements.push(&user_settings.content);
+            if let Some(release_channel) = user_settings.for_release_channel() {
+                refinements.push(release_channel)
             }
-
-            let server_value = self
-                .raw_server_settings
-                .as_ref()
-                .and_then(|server_setting| {
-                    setting_value.deserialize_setting(server_setting).log_err()
-                });
-
-            let extension_value = setting_value
-                .deserialize_setting(&self.raw_extension_settings)
-                .log_err();
-
-            if let Some(setting) = setting_value
-                .load_setting(
-                    SettingsSources {
-                        default: &default_settings,
-                        global: None,
-                        extensions: extension_value.as_ref(),
-                        user: user_value.as_ref(),
-                        release_channel: release_channel_value.as_ref(),
-                        operating_system: os_settings_value.as_ref(),
-                        profile: profile_value.as_ref(),
-                        server: server_value.as_ref(),
-                        project: &[],
-                    },
-                    cx,
-                )
-                .context("A default setting must be added to the `default.json` file")
-                .log_err()
-            {
-                setting_value.set_global_value(setting);
+            if let Some(os) = user_settings.for_os() {
+                refinements.push(os)
             }
+            if let Some(profile) = user_settings.for_profile(cx) {
+                refinements.push(profile)
+            }
+        }
+
+        if let Some(server_settings) = self.server_settings.as_ref() {
+            refinements.push(server_settings)
         }
+        let default = self.default_settings.as_ref().unwrap();
+        // todo!() unwrap...
+        let mut value = T::from_file(default).unwrap();
+        for refinement in refinements {
+            value.refine(refinement)
+        }
+
+        setting_value.set_global_value(Box::new(value));
+
+        // todo!() local settings
+        // (they weren't handled before...)
     }
 
     /// Get the value of a setting.
@@ -476,35 +415,31 @@ impl SettingsStore {
     ///
     /// For user-facing functionality use the typed setting interface.
     /// (e.g. ProjectSettings::get_global(cx))
-    pub fn raw_user_settings(&self) -> &Value {
-        &self.raw_user_settings
+    pub fn raw_user_settings(&self) -> Option<&UserSettingsContent> {
+        self.user_settings.as_ref()
     }
 
     /// Replaces current settings with the values from the given JSON.
-    pub fn set_raw_user_settings(&mut self, new_settings: Value, cx: &mut App) -> Result<()> {
-        self.raw_user_settings = new_settings;
+    pub fn set_raw_user_settings(
+        &mut self,
+        new_settings: UserSettingsContent,
+        cx: &mut App,
+    ) -> Result<()> {
+        self.user_settings = Some(new_settings);
         self.recompute_values(None, cx)?;
         Ok(())
     }
 
     /// Get the configured settings profile names.
     pub fn configured_settings_profiles(&self) -> impl Iterator<Item = &str> {
-        self.raw_user_settings
-            .get("profiles")
-            .and_then(|v| v.as_object())
-            .into_iter()
-            .flat_map(|obj| obj.keys())
-            .map(|s| s.as_str())
-    }
-
-    /// Access the raw JSON value of the global settings.
-    pub fn raw_global_settings(&self) -> Option<&Value> {
-        self.raw_global_settings.as_ref()
+        self.user_settings
+            .iter()
+            .flat_map(|settings| settings.profiles.keys().map(|k| k.as_str()))
     }
 
     /// Access the raw JSON value of the default settings.
-    pub fn raw_default_settings(&self) -> &Value {
-        &self.raw_default_settings
+    pub fn raw_default_settings(&self) -> Option<&SettingsContent> {
+        self.default_settings.as_ref()
     }
 
     #[cfg(any(test, feature = "test-support"))]
@@ -521,13 +456,18 @@ impl SettingsStore {
     /// This is only for tests. Normally, settings are only loaded from
     /// JSON files.
     #[cfg(any(test, feature = "test-support"))]
-    pub fn update_user_settings<T: Settings>(
+    pub fn update_user_settings(
         &mut self,
         cx: &mut App,
-        update: impl FnOnce(&mut T::FileContent),
+        update: impl FnOnce(&mut SettingsContent),
     ) {
-        let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();
-        let new_text = self.new_text_for_update::<T>(old_text, update);
+        let mut content = self.user_settings.as_ref().unwrap().content.clone();
+        update(&mut content);
+        let new_text = serde_json::to_string(&UserSettingsContent {
+            content,
+            ..Default::default()
+        })
+        .unwrap();
         self.set_user_settings(&new_text, cx).unwrap();
     }
 
@@ -626,14 +566,14 @@ impl SettingsStore {
         self.update_settings_file_inner(fs, update)
     }
 
-    pub fn update_settings_file<T: Settings>(
+    pub fn update_settings_file(
         &self,
         fs: Arc<dyn Fs>,
-        update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
+        update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
     ) {
         _ = self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| {
             cx.read_global(|store: &SettingsStore, cx| {
-                store.new_text_for_update::<T>(old_text, |content| update(content, cx))
+                store.new_text_for_update(old_text, |content| update(content, cx))
             })
         });
     }
@@ -660,12 +600,12 @@ impl SettingsStore {
 impl SettingsStore {
     /// Updates the value of a setting in a JSON file, returning the new text
     /// for that JSON file.
-    pub fn new_text_for_update<T: Settings>(
+    pub fn new_text_for_update(
         &self,
         old_text: String,
-        update: impl FnOnce(&mut T::FileContent),
+        update: impl FnOnce(&mut SettingsContent),
     ) -> String {
-        let edits = self.edits_for_update::<T>(&old_text, update);
+        let edits = self.edits_for_update(&old_text, update);
         let mut new_text = old_text;
         for (range, replacement) in edits.into_iter() {
             new_text.replace_range(range, &replacement);
@@ -689,36 +629,19 @@ impl SettingsStore {
 
     /// Updates the value of a setting in a JSON file, returning a list
     /// of edits to apply to the JSON file.
-    pub fn edits_for_update<T: Settings>(
+    pub fn edits_for_update(
         &self,
         text: &str,
-        update: impl FnOnce(&mut T::FileContent),
+        update: impl FnOnce(&mut SettingsContent),
     ) -> Vec<(Range<usize>, String)> {
-        let setting_type_id = TypeId::of::<T>();
-
-        let preserved_keys = T::PRESERVED_KEYS.unwrap_or_default();
-
-        let setting = self
-            .setting_values
-            .get(&setting_type_id)
-            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()));
-        let raw_settings = parse_json_with_comments::<Value>(text).unwrap_or_default();
-        let (key, deserialized_setting) = setting.deserialize_setting_with_key(&raw_settings);
-        let old_content = match deserialized_setting {
-            Ok(content) => content.0.downcast::<T::FileContent>().unwrap(),
-            Err(_) => Box::<<T as Settings>::FileContent>::default(),
-        };
+        let old_content: UserSettingsContent = serde_json::from_str(text).unwrap_or_default();
         let mut new_content = old_content.clone();
-        update(&mut new_content);
+        update(&mut new_content.content);
 
         let old_value = serde_json::to_value(&old_content).unwrap();
         let new_value = serde_json::to_value(new_content).unwrap();
 
         let mut key_path = Vec::new();
-        if let Some(key) = key {
-            key_path.push(key);
-        }
-
         let mut edits = Vec::new();
         let tab_size = self.json_tab_size();
         let mut text = text.to_string();
@@ -728,35 +651,14 @@ impl SettingsStore {
             tab_size,
             &old_value,
             &new_value,
-            preserved_keys,
+            &[], // todo!() is this still needed?
             &mut edits,
         );
         edits
     }
 
-    /// Configure the tab sized when updating JSON files.
-    pub fn set_json_tab_size_callback<T: Settings>(
-        &mut self,
-        get_tab_size: fn(&T) -> Option<usize>,
-    ) {
-        self.tab_size_callback = Some((
-            TypeId::of::<T>(),
-            Box::new(move |value| get_tab_size(value.downcast_ref::<T>().unwrap())),
-        ));
-    }
-
     pub fn json_tab_size(&self) -> usize {
-        const DEFAULT_JSON_TAB_SIZE: usize = 2;
-
-        if let Some((setting_type_id, callback)) = &self.tab_size_callback {
-            let setting_value = self.setting_values.get(setting_type_id).unwrap();
-            let value = setting_value.value_for_path(None);
-            if let Some(value) = callback(value) {
-                return value;
-            }
-        }
-
-        DEFAULT_JSON_TAB_SIZE
+        2
     }
 
     /// Sets the default settings via a JSON string.
@@ -767,29 +669,22 @@ impl SettingsStore {
         default_settings_content: &str,
         cx: &mut App,
     ) -> Result<()> {
-        let settings: Value = parse_json_with_comments(default_settings_content)?;
-        anyhow::ensure!(settings.is_object(), "settings must be an object");
-        self.raw_default_settings = settings;
+        self.default_settings = parse_json_with_comments(default_settings_content)?;
         self.recompute_values(None, cx)?;
         Ok(())
     }
 
     /// Sets the user settings via a JSON string.
-    pub fn set_user_settings(
-        &mut self,
-        user_settings_content: &str,
-        cx: &mut App,
-    ) -> Result<Value> {
-        let settings: Value = if user_settings_content.is_empty() {
+    pub fn set_user_settings(&mut self, user_settings_content: &str, cx: &mut App) -> Result<()> {
+        let settings: UserSettingsContent = if user_settings_content.is_empty() {
             parse_json_with_comments("{}")?
         } else {
             parse_json_with_comments(user_settings_content)?
         };
 
-        anyhow::ensure!(settings.is_object(), "settings must be an object");
-        self.raw_user_settings = settings.clone();
+        self.user_settings = Some(settings);
         self.recompute_values(None, cx)?;
-        Ok(settings)
+        Ok(())
     }
 
     /// Sets the global settings via a JSON string.
@@ -797,17 +692,16 @@ impl SettingsStore {
         &mut self,
         global_settings_content: &str,
         cx: &mut App,
-    ) -> Result<Value> {
-        let settings: Value = if global_settings_content.is_empty() {
+    ) -> Result<()> {
+        let settings: SettingsContent = if global_settings_content.is_empty() {
             parse_json_with_comments("{}")?
         } else {
             parse_json_with_comments(global_settings_content)?
         };
 
-        anyhow::ensure!(settings.is_object(), "settings must be an object");
-        self.raw_global_settings = Some(settings.clone());
+        self.global_settings = Some(settings);
         self.recompute_values(None, cx)?;
-        Ok(settings)
+        Ok(())
     }
 
     pub fn set_server_settings(
@@ -815,20 +709,14 @@ impl SettingsStore {
         server_settings_content: &str,
         cx: &mut App,
     ) -> Result<()> {
-        let settings: Option<Value> = if server_settings_content.is_empty() {
+        let settings: Option<ServerSettingsContent> = if server_settings_content.is_empty() {
             None
         } else {
             parse_json_with_comments(server_settings_content)?
         };
 
-        anyhow::ensure!(
-            settings
-                .as_ref()
-                .map(|value| value.is_object())
-                .unwrap_or(true),
-            "settings must be an object"
-        );
-        self.raw_server_settings = settings;
+        todo!();
+        // self.server_settings = Some(settings);
         self.recompute_values(None, cx)?;
         Ok(())
     }
@@ -864,7 +752,7 @@ impl SettingsStore {
             }
             (LocalSettingsKind::Settings, None) => {
                 zed_settings_changed = self
-                    .raw_local_settings
+                    .local_settings
                     .remove(&(root_id, directory_path.clone()))
                     .is_some()
             }
@@ -873,24 +761,27 @@ impl SettingsStore {
                     .remove(&(root_id, directory_path.clone()));
             }
             (LocalSettingsKind::Settings, Some(settings_contents)) => {
-                let new_settings =
-                    parse_json_with_comments::<Value>(settings_contents).map_err(|e| {
-                        InvalidSettingsError::LocalSettings {
-                            path: directory_path.join(local_settings_file_relative_path()),
-                            message: e.to_string(),
-                        }
-                    })?;
-                match self
-                    .raw_local_settings
-                    .entry((root_id, directory_path.clone()))
-                {
+                let new_settings = parse_json_with_comments::<ProjectSettingsContent>(
+                    settings_contents,
+                )
+                .map_err(|e| InvalidSettingsError::LocalSettings {
+                    path: directory_path.join(local_settings_file_relative_path()),
+                    message: e.to_string(),
+                })?;
+                match self.local_settings.entry((root_id, directory_path.clone())) {
                     btree_map::Entry::Vacant(v) => {
-                        v.insert(new_settings);
+                        v.insert(SettingsContent {
+                            project: new_settings,
+                            ..Default::default()
+                        });
                         zed_settings_changed = true;
                     }
                     btree_map::Entry::Occupied(mut o) => {
-                        if o.get() != &new_settings {
-                            o.insert(new_settings);
+                        if &o.get().project != &new_settings {
+                            o.insert(SettingsContent {
+                                project: new_settings,
+                                ..Default::default()
+                            });
                             zed_settings_changed = true;
                         }
                     }
@@ -942,17 +833,24 @@ impl SettingsStore {
         Ok(())
     }
 
-    pub fn set_extension_settings<T: Serialize>(&mut self, content: T, cx: &mut App) -> Result<()> {
-        let settings: Value = serde_json::to_value(content)?;
-        anyhow::ensure!(settings.is_object(), "settings must be an object");
-        self.raw_extension_settings = settings;
+    pub fn set_extension_settings(
+        &mut self,
+        content: ExtensionsSettingsContent,
+        cx: &mut App,
+    ) -> Result<()> {
+        self.extension_settings = Some(SettingsContent {
+            project: ProjectSettingsContent {
+                all_languages: content.all_languages,
+            },
+            ..Default::default()
+        });
         self.recompute_values(None, cx)?;
         Ok(())
     }
 
     /// Add or remove a set of local settings via a JSON string.
     pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
-        self.raw_local_settings
+        self.local_settings
             .retain(|(worktree_id, _), _| worktree_id != &root_id);
         self.recompute_values(Some((root_id, "".as_ref())), cx)?;
         Ok(())
@@ -962,7 +860,7 @@ impl SettingsStore {
         &self,
         root_id: WorktreeId,
     ) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
-        self.raw_local_settings
+        self.local_settings
             .range(
                 (root_id, Path::new("").into())
                     ..(
@@ -991,193 +889,194 @@ impl SettingsStore {
     }
 
     pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams, cx: &App) -> Value {
-        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
-            .with_transform(DefaultDenyUnknownFields)
-            .into_generator();
-        let mut combined_schema = json!({
-            "type": "object",
-            "properties": {}
-        });
-
-        // Merge together settings schemas, similarly to json schema's "allOf". This merging is
-        // recursive, though at time of writing this recursive nature isn't used very much. An
-        // example of it is the schema for `jupyter` having contribution from both `EditorSettings`
-        // and `JupyterSettings`.
-        //
-        // This logic could be removed in favor of "allOf", but then there isn't the opportunity to
-        // validate and fully control the merge.
-        for setting_value in self.setting_values.values() {
-            let mut setting_schema = setting_value.json_schema(&mut generator);
-
-            if let Some(key) = setting_value.key() {
-                if let Some(properties) = combined_schema.get_mut("properties")
-                    && let Some(properties_obj) = properties.as_object_mut()
-                {
-                    if let Some(target) = properties_obj.get_mut(key) {
-                        merge_schema(target, setting_schema.to_value());
-                    } else {
-                        properties_obj.insert(key.to_string(), setting_schema.to_value());
-                    }
-                }
-            } else {
-                setting_schema.remove("description");
-                setting_schema.remove("additionalProperties");
-                merge_schema(&mut combined_schema, setting_schema.to_value());
-            }
-        }
-
-        fn merge_schema(target: &mut serde_json::Value, source: serde_json::Value) {
-            let (Some(target_obj), serde_json::Value::Object(source_obj)) =
-                (target.as_object_mut(), source)
-            else {
-                return;
-            };
-
-            for (source_key, source_value) in source_obj {
-                match source_key.as_str() {
-                    "properties" => {
-                        let serde_json::Value::Object(source_properties) = source_value else {
-                            log::error!(
-                                "bug: expected object for `{}` json schema field, but got: {}",
-                                source_key,
-                                source_value
-                            );
-                            continue;
-                        };
-                        let target_properties =
-                            target_obj.entry(source_key.clone()).or_insert(json!({}));
-                        let Some(target_properties) = target_properties.as_object_mut() else {
-                            log::error!(
-                                "bug: expected object for `{}` json schema field, but got: {}",
-                                source_key,
-                                target_properties
-                            );
-                            continue;
-                        };
-                        for (key, value) in source_properties {
-                            if let Some(existing) = target_properties.get_mut(&key) {
-                                merge_schema(existing, value);
-                            } else {
-                                target_properties.insert(key, value);
-                            }
-                        }
-                    }
-                    "allOf" | "anyOf" | "oneOf" => {
-                        let serde_json::Value::Array(source_array) = source_value else {
-                            log::error!(
-                                "bug: expected array for `{}` json schema field, but got: {}",
-                                source_key,
-                                source_value,
-                            );
-                            continue;
-                        };
-                        let target_array =
-                            target_obj.entry(source_key.clone()).or_insert(json!([]));
-                        let Some(target_array) = target_array.as_array_mut() else {
-                            log::error!(
-                                "bug: expected array for `{}` json schema field, but got: {}",
-                                source_key,
-                                target_array,
-                            );
-                            continue;
-                        };
-                        target_array.extend(source_array);
-                    }
-                    "type"
-                    | "$ref"
-                    | "enum"
-                    | "minimum"
-                    | "maximum"
-                    | "pattern"
-                    | "description"
-                    | "additionalProperties" => {
-                        if let Some(old_value) =
-                            target_obj.insert(source_key.clone(), source_value.clone())
-                            && old_value != source_value
-                        {
-                            log::error!(
-                                "bug: while merging JSON schemas, \
-                                    mismatch `\"{}\": {}` (before was `{}`)",
-                                source_key,
-                                old_value,
-                                source_value
-                            );
-                        }
-                    }
-                    _ => {
-                        log::error!(
-                            "bug: while merging settings JSON schemas, \
-                            encountered unexpected `\"{}\": {}`",
-                            source_key,
-                            source_value
-                        );
-                    }
-                }
-            }
-        }
-
-        // add schemas which are determined at runtime
-        for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
-            (parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
-        }
-
-        // add merged settings schema to the definitions
-        const ZED_SETTINGS: &str = "ZedSettings";
-        let zed_settings_ref = add_new_subschema(&mut generator, ZED_SETTINGS, combined_schema);
-
-        // add `ZedSettingsOverride` which is the same as `ZedSettings` except that unknown
-        // fields are rejected. This is used for release stage settings and profiles.
-        let mut zed_settings_override = zed_settings_ref.clone();
-        zed_settings_override.insert("unevaluatedProperties".to_string(), false.into());
-        let zed_settings_override_ref = add_new_subschema(
-            &mut generator,
-            "ZedSettingsOverride",
-            zed_settings_override.to_value(),
-        );
-
-        // Remove `"additionalProperties": false` added by `DefaultDenyUnknownFields` so that
-        // unknown fields can be handled by the root schema and `ZedSettingsOverride`.
-        let mut definitions = generator.take_definitions(true);
-        definitions
-            .get_mut(ZED_SETTINGS)
-            .unwrap()
-            .as_object_mut()
-            .unwrap()
-            .remove("additionalProperties");
-
-        let meta_schema = generator
-            .settings()
-            .meta_schema
-            .as_ref()
-            .expect("meta_schema should be present in schemars settings")
-            .to_string();
-
-        json!({
-            "$schema": meta_schema,
-            "title": "Zed Settings",
-            "unevaluatedProperties": false,
-            // ZedSettings + settings overrides for each release stage / OS / profiles
-            "allOf": [
-                zed_settings_ref,
-                {
-                    "properties": {
-                        "dev": zed_settings_override_ref,
-                        "nightly": zed_settings_override_ref,
-                        "stable": zed_settings_override_ref,
-                        "preview": zed_settings_override_ref,
-                        "linux": zed_settings_override_ref,
-                        "macos": zed_settings_override_ref,
-                        "windows": zed_settings_override_ref,
-                        "profiles": {
-                            "type": "object",
-                            "description": "Configures any number of settings profiles.",
-                            "additionalProperties": zed_settings_override_ref
-                        }
-                    }
-                }
-            ],
-            "$defs": definitions,
-        })
+        todo!()
+        // let mut generator = schemars::generate::SchemaSettings::draft2019_09()
+        //     .with_transform(DefaultDenyUnknownFields)
+        //     .into_generator();
+        // let mut combined_schema = json!({
+        //     "type": "object",
+        //     "properties": {}
+        // });
+
+        // // Merge together settings schemas, similarly to json schema's "allOf". This merging is
+        // // recursive, though at time of writing this recursive nature isn't used very much. An
+        // // example of it is the schema for `jupyter` having contribution from both `EditorSettings`
+        // // and `JupyterSettings`.
+        // //
+        // // This logic could be removed in favor of "allOf", but then there isn't the opportunity to
+        // // validate and fully control the merge.
+        // for setting_value in self.setting_values.values() {
+        //     let mut setting_schema = setting_value.json_schema(&mut generator);
+
+        //     if let Some(key) = setting_value.key() {
+        //         if let Some(properties) = combined_schema.get_mut("properties")
+        //             && let Some(properties_obj) = properties.as_object_mut()
+        //         {
+        //             if let Some(target) = properties_obj.get_mut(key) {
+        //                 merge_schema(target, setting_schema.to_value());
+        //             } else {
+        //                 properties_obj.insert(key.to_string(), setting_schema.to_value());
+        //             }
+        //         }
+        //     } else {
+        //         setting_schema.remove("description");
+        //         setting_schema.remove("additionalProperties");
+        //         merge_schema(&mut combined_schema, setting_schema.to_value());
+        //     }
+        // }
+
+        // fn merge_schema(target: &mut serde_json::Value, source: serde_json::Value) {
+        //     let (Some(target_obj), serde_json::Value::Object(source_obj)) =
+        //         (target.as_object_mut(), source)
+        //     else {
+        //         return;
+        //     };
+
+        //     for (source_key, source_value) in source_obj {
+        //         match source_key.as_str() {
+        //             "properties" => {
+        //                 let serde_json::Value::Object(source_properties) = source_value else {
+        //                     log::error!(
+        //                         "bug: expected object for `{}` json schema field, but got: {}",
+        //                         source_key,
+        //                         source_value
+        //                     );
+        //                     continue;
+        //                 };
+        //                 let target_properties =
+        //                     target_obj.entry(source_key.clone()).or_insert(json!({}));
+        //                 let Some(target_properties) = target_properties.as_object_mut() else {
+        //                     log::error!(
+        //                         "bug: expected object for `{}` json schema field, but got: {}",
+        //                         source_key,
+        //                         target_properties
+        //                     );
+        //                     continue;
+        //                 };
+        //                 for (key, value) in source_properties {
+        //                     if let Some(existing) = target_properties.get_mut(&key) {
+        //                         merge_schema(existing, value);
+        //                     } else {
+        //                         target_properties.insert(key, value);
+        //                     }
+        //                 }
+        //             }
+        //             "allOf" | "anyOf" | "oneOf" => {
+        //                 let serde_json::Value::Array(source_array) = source_value else {
+        //                     log::error!(
+        //                         "bug: expected array for `{}` json schema field, but got: {}",
+        //                         source_key,
+        //                         source_value,
+        //                     );
+        //                     continue;
+        //                 };
+        //                 let target_array =
+        //                     target_obj.entry(source_key.clone()).or_insert(json!([]));
+        //                 let Some(target_array) = target_array.as_array_mut() else {
+        //                     log::error!(
+        //                         "bug: expected array for `{}` json schema field, but got: {}",
+        //                         source_key,
+        //                         target_array,
+        //                     );
+        //                     continue;
+        //                 };
+        //                 target_array.extend(source_array);
+        //             }
+        //             "type"
+        //             | "$ref"
+        //             | "enum"
+        //             | "minimum"
+        //             | "maximum"
+        //             | "pattern"
+        //             | "description"
+        //             | "additionalProperties" => {
+        //                 if let Some(old_value) =
+        //                     target_obj.insert(source_key.clone(), source_value.clone())
+        //                     && old_value != source_value
+        //                 {
+        //                     log::error!(
+        //                         "bug: while merging JSON schemas, \
+        //                             mismatch `\"{}\": {}` (before was `{}`)",
+        //                         source_key,
+        //                         old_value,
+        //                         source_value
+        //                     );
+        //                 }
+        //             }
+        //             _ => {
+        //                 log::error!(
+        //                     "bug: while merging settings JSON schemas, \
+        //                     encountered unexpected `\"{}\": {}`",
+        //                     source_key,
+        //                     source_value
+        //                 );
+        //             }
+        //         }
+        //     }
+        // }
+
+        // // add schemas which are determined at runtime
+        // for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
+        //     (parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
+        // }
+
+        // // add merged settings schema to the definitions
+        // const ZED_SETTINGS: &str = "ZedSettings";
+        // let zed_settings_ref = add_new_subschema(&mut generator, ZED_SETTINGS, combined_schema);
+
+        // // add `ZedSettingsOverride` which is the same as `ZedSettings` except that unknown
+        // // fields are rejected. This is used for release stage settings and profiles.
+        // let mut zed_settings_override = zed_settings_ref.clone();
+        // zed_settings_override.insert("unevaluatedProperties".to_string(), false.into());
+        // let zed_settings_override_ref = add_new_subschema(
+        //     &mut generator,
+        //     "ZedSettingsOverride",
+        //     zed_settings_override.to_value(),
+        // );
+
+        // // Remove `"additionalProperties": false` added by `DefaultDenyUnknownFields` so that
+        // // unknown fields can be handled by the root schema and `ZedSettingsOverride`.
+        // let mut definitions = generator.take_definitions(true);
+        // definitions
+        //     .get_mut(ZED_SETTINGS)
+        //     .unwrap()
+        //     .as_object_mut()
+        //     .unwrap()
+        //     .remove("additionalProperties");
+
+        // let meta_schema = generator
+        //     .settings()
+        //     .meta_schema
+        //     .as_ref()
+        //     .expect("meta_schema should be present in schemars settings")
+        //     .to_string();
+
+        // json!({
+        //     "$schema": meta_schema,
+        //     "title": "Zed Settings",
+        //     "unevaluatedProperties": false,
+        //     // ZedSettings + settings overrides for each release stage / OS / profiles
+        //     "allOf": [
+        //         zed_settings_ref,
+        //         {
+        //             "properties": {
+        //                 "dev": zed_settings_override_ref,
+        //                 "nightly": zed_settings_override_ref,
+        //                 "stable": zed_settings_override_ref,
+        //                 "preview": zed_settings_override_ref,
+        //                 "linux": zed_settings_override_ref,
+        //                 "macos": zed_settings_override_ref,
+        //                 "windows": zed_settings_override_ref,
+        //                 "profiles": {
+        //                     "type": "object",
+        //                     "description": "Configures any number of settings profiles.",
+        //                     "additionalProperties": zed_settings_override_ref
+        //                 }
+        //             }
+        //         }
+        //     ],
+        //     "$defs": definitions,
+        // })
     }
 
     fn recompute_values(
@@ -1186,90 +1085,45 @@ impl SettingsStore {
         cx: &mut App,
     ) -> std::result::Result<(), InvalidSettingsError> {
         // Reload the global and local values for every setting.
-        let mut project_settings_stack = Vec::<DeserializedSetting>::new();
+        let mut project_settings_stack = Vec::<&SettingsContent>::new();
         let mut paths_stack = Vec::<Option<(WorktreeId, &Path)>>::new();
-        for setting_value in self.setting_values.values_mut() {
-            let default_settings = setting_value
-                .deserialize_setting(&self.raw_default_settings)
-                .map_err(|e| InvalidSettingsError::DefaultSettings {
-                    message: e.to_string(),
-                })?;
 
-            let global_settings = self
-                .raw_global_settings
-                .as_ref()
-                .and_then(|setting| setting_value.deserialize_setting(setting).log_err());
-
-            let extension_settings = setting_value
-                .deserialize_setting(&self.raw_extension_settings)
-                .log_err();
-
-            let user_settings = match setting_value.deserialize_setting(&self.raw_user_settings) {
-                Ok(settings) => Some(settings),
-                Err(error) => {
-                    return Err(InvalidSettingsError::UserSettings {
-                        message: error.to_string(),
-                    });
-                }
-            };
-
-            let server_settings = self
-                .raw_server_settings
-                .as_ref()
-                .and_then(|setting| setting_value.deserialize_setting(setting).log_err());
-
-            let mut release_channel_settings = None;
-            if let Some(release_settings) = &self
-                .raw_user_settings
-                .get(release_channel::RELEASE_CHANNEL.dev_name())
-                && let Some(release_settings) = setting_value
-                    .deserialize_setting(release_settings)
-                    .log_err()
-            {
-                release_channel_settings = Some(release_settings);
-            }
+        let mut refinements = Vec::default();
 
-            let mut os_settings = None;
-            if let Some(settings) = &self.raw_user_settings.get(env::consts::OS)
-                && let Some(settings) = setting_value.deserialize_setting(settings).log_err()
-            {
-                os_settings = Some(settings);
-            }
+        if let Some(extension_settings) = self.extension_settings.as_ref() {
+            refinements.push(extension_settings)
+        }
 
-            let mut profile_settings = None;
-            if let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>()
-                && let Some(profiles) = self.raw_user_settings.get("profiles")
-                && let Some(profile_json) = profiles.get(&active_profile.0)
-            {
-                profile_settings = setting_value.deserialize_setting(profile_json).log_err();
+        if let Some(user_settings) = self.user_settings.as_ref() {
+            refinements.push(&user_settings.content);
+            if let Some(release_channel) = user_settings.for_release_channel() {
+                refinements.push(release_channel)
+            }
+            if let Some(os) = user_settings.for_os() {
+                refinements.push(os)
             }
+            if let Some(profile) = user_settings.for_profile(cx) {
+                refinements.push(profile)
+            }
+        }
+
+        if let Some(server_settings) = self.server_settings.as_ref() {
+            refinements.push(server_settings)
+        }
+        let default = self.default_settings.as_ref().unwrap();
 
+        for setting_value in self.setting_values.values_mut() {
             // If the global settings file changed, reload the global value for the field.
-            if changed_local_path.is_none()
-                && let Some(value) = setting_value
-                    .load_setting(
-                        SettingsSources {
-                            default: &default_settings,
-                            global: global_settings.as_ref(),
-                            extensions: extension_settings.as_ref(),
-                            user: user_settings.as_ref(),
-                            release_channel: release_channel_settings.as_ref(),
-                            operating_system: os_settings.as_ref(),
-                            profile: profile_settings.as_ref(),
-                            server: server_settings.as_ref(),
-                            project: &[],
-                        },
-                        cx,
-                    )
-                    .log_err()
-            {
+            if changed_local_path.is_none() {
+                let mut value = setting_value.from_file(&default).unwrap();
+                setting_value.refine(&mut value, &refinements);
                 setting_value.set_global_value(value);
             }
 
             // Reload the local values for the setting.
             paths_stack.clear();
             project_settings_stack.clear();
-            for ((root_id, directory_path), local_settings) in &self.raw_local_settings {
+            for ((root_id, directory_path), local_settings) in &self.local_settings {
                 // Build a stack of all of the local values for that setting.
                 while let Some(prev_entry) = paths_stack.last() {
                     if let Some((prev_root_id, prev_path)) = prev_entry