settings_store.rs

   1use anyhow::{Context as _, Result};
   2use collections::{BTreeMap, HashMap, btree_map, hash_map};
   3use ec4rs::{ConfigParser, PropertiesSource, Section};
   4use fs::Fs;
   5use futures::{
   6    FutureExt, StreamExt,
   7    channel::{mpsc, oneshot},
   8    future::LocalBoxFuture,
   9};
  10use gpui::{App, AsyncApp, BorrowAppContext, Global, SharedString, Task, UpdateGlobal};
  11
  12use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};
  13use schemars::JsonSchema;
  14use serde::{Serialize, de::DeserializeOwned};
  15use serde_json::{Value, json};
  16use smallvec::SmallVec;
  17use std::{
  18    any::{Any, TypeId, type_name},
  19    env,
  20    fmt::Debug,
  21    ops::Range,
  22    path::{Path, PathBuf},
  23    str::{self, FromStr},
  24    sync::Arc,
  25};
  26use util::{
  27    ResultExt as _, merge_non_null_json_value_into,
  28    schemars::{DefaultDenyUnknownFields, add_new_subschema},
  29};
  30
  31pub type EditorconfigProperties = ec4rs::Properties;
  32
  33use crate::{
  34    ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUiEntry,
  35    VsCodeSettings, WorktreeId, parse_json_with_comments, replace_value_in_json_text,
  36    settings_ui_core::SettingsUi, update_value_in_json_text,
  37};
  38
  39pub trait SettingsKey: 'static + Send + Sync {
  40    /// The name of a key within the JSON file from which this setting should
  41    /// be deserialized. If this is `None`, then the setting will be deserialized
  42    /// from the root object.
  43    const KEY: Option<&'static str>;
  44
  45    const FALLBACK_KEY: Option<&'static str> = None;
  46}
  47
  48/// A value that can be defined as a user setting.
  49///
  50/// Settings can be loaded from a combination of multiple JSON files.
  51pub trait Settings: 'static + Send + Sync {
  52    /// The name of the keys in the [`FileContent`](Self::FileContent) that should
  53    /// always be written to a settings file, even if their value matches the default
  54    /// value.
  55    ///
  56    /// This is useful for tagged [`FileContent`](Self::FileContent)s where the tag
  57    /// is a "version" field that should always be persisted, even if the current
  58    /// user settings match the current version of the settings.
  59    const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
  60
  61    /// The type that is stored in an individual JSON file.
  62    type FileContent: Clone
  63        + Default
  64        + Serialize
  65        + DeserializeOwned
  66        + JsonSchema
  67        + SettingsUi
  68        + SettingsKey;
  69
  70    /*
  71     *  let path = Settings
  72     *
  73     *
  74     */
  75    /// The logic for combining together values from one or more JSON files into the
  76    /// final value for this setting.
  77    ///
  78    /// # Warning
  79    /// `Self::FileContent` deserialized field names should match with `Self` deserialized field names
  80    /// otherwise the field won't be deserialized properly and you will get the error:
  81    /// "A default setting must be added to the `default.json` file"
  82    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self>
  83    where
  84        Self: Sized;
  85
  86    fn missing_default() -> anyhow::Error {
  87        anyhow::anyhow!("missing default for: {}", std::any::type_name::<Self>())
  88    }
  89
  90    /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known
  91    /// equivalent settings from a vscode config to our config
  92    fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent);
  93
  94    #[track_caller]
  95    fn register(cx: &mut App)
  96    where
  97        Self: Sized,
  98    {
  99        SettingsStore::update_global(cx, |store, cx| {
 100            store.register_setting::<Self>(cx);
 101        });
 102    }
 103
 104    #[track_caller]
 105    fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self
 106    where
 107        Self: Sized,
 108    {
 109        cx.global::<SettingsStore>().get(path)
 110    }
 111
 112    #[track_caller]
 113    fn get_global(cx: &App) -> &Self
 114    where
 115        Self: Sized,
 116    {
 117        cx.global::<SettingsStore>().get(None)
 118    }
 119
 120    #[track_caller]
 121    fn try_get(cx: &App) -> Option<&Self>
 122    where
 123        Self: Sized,
 124    {
 125        if cx.has_global::<SettingsStore>() {
 126            cx.global::<SettingsStore>().try_get(None)
 127        } else {
 128            None
 129        }
 130    }
 131
 132    #[track_caller]
 133    fn try_read_global<R>(cx: &AsyncApp, f: impl FnOnce(&Self) -> R) -> Option<R>
 134    where
 135        Self: Sized,
 136    {
 137        cx.try_read_global(|s: &SettingsStore, _| f(s.get(None)))
 138    }
 139
 140    #[track_caller]
 141    fn override_global(settings: Self, cx: &mut App)
 142    where
 143        Self: Sized,
 144    {
 145        cx.global_mut::<SettingsStore>().override_global(settings)
 146    }
 147}
 148
 149#[derive(Clone, Copy, Debug)]
 150pub struct SettingsSources<'a, T> {
 151    /// The default Zed settings.
 152    pub default: &'a T,
 153    /// Global settings (loaded before user settings).
 154    pub global: Option<&'a T>,
 155    /// Settings provided by extensions.
 156    pub extensions: Option<&'a T>,
 157    /// The user settings.
 158    pub user: Option<&'a T>,
 159    /// The user settings for the current release channel.
 160    pub release_channel: Option<&'a T>,
 161    /// The user settings for the current operating system.
 162    pub operating_system: Option<&'a T>,
 163    /// The settings associated with an enabled settings profile
 164    pub profile: Option<&'a T>,
 165    /// The server's settings.
 166    pub server: Option<&'a T>,
 167    /// The project settings, ordered from least specific to most specific.
 168    pub project: &'a [&'a T],
 169}
 170
 171impl<'a, T: Serialize> SettingsSources<'a, T> {
 172    /// Returns an iterator over the default settings as well as all settings customizations.
 173    pub fn defaults_and_customizations(&self) -> impl Iterator<Item = &T> {
 174        [self.default].into_iter().chain(self.customizations())
 175    }
 176
 177    /// Returns an iterator over all of the settings customizations.
 178    pub fn customizations(&self) -> impl Iterator<Item = &T> {
 179        self.global
 180            .into_iter()
 181            .chain(self.extensions)
 182            .chain(self.user)
 183            .chain(self.release_channel)
 184            .chain(self.operating_system)
 185            .chain(self.profile)
 186            .chain(self.server)
 187            .chain(self.project.iter().copied())
 188    }
 189
 190    /// Returns the settings after performing a JSON merge of the provided customizations.
 191    ///
 192    /// Customizations later in the iterator win out over the earlier ones.
 193    pub fn json_merge_with<O: DeserializeOwned>(
 194        customizations: impl Iterator<Item = &'a T>,
 195    ) -> Result<O> {
 196        let mut merged = Value::Null;
 197        for value in customizations {
 198            merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
 199        }
 200        Ok(serde_json::from_value(merged)?)
 201    }
 202
 203    /// Returns the settings after performing a JSON merge of the customizations into the
 204    /// default settings.
 205    ///
 206    /// More-specific customizations win out over the less-specific ones.
 207    pub fn json_merge<O: DeserializeOwned>(&'a self) -> Result<O> {
 208        Self::json_merge_with(self.defaults_and_customizations())
 209    }
 210}
 211
 212#[derive(Clone, Copy, Debug)]
 213pub struct SettingsLocation<'a> {
 214    pub worktree_id: WorktreeId,
 215    pub path: &'a Path,
 216}
 217
 218/// A set of strongly-typed setting values defined via multiple config files.
 219pub struct SettingsStore {
 220    setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
 221    raw_default_settings: Value,
 222    raw_global_settings: Option<Value>,
 223    raw_user_settings: Value,
 224    raw_server_settings: Option<Value>,
 225    raw_extension_settings: Value,
 226    raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), Value>,
 227    raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>,
 228    tab_size_callback: Option<(
 229        TypeId,
 230        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,
 231    )>,
 232    _setting_file_updates: Task<()>,
 233    setting_file_updates_tx:
 234        mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,
 235}
 236
 237#[derive(Clone)]
 238pub struct Editorconfig {
 239    pub is_root: bool,
 240    pub sections: SmallVec<[Section; 5]>,
 241}
 242
 243impl FromStr for Editorconfig {
 244    type Err = anyhow::Error;
 245
 246    fn from_str(contents: &str) -> Result<Self, Self::Err> {
 247        let parser = ConfigParser::new_buffered(contents.as_bytes())
 248            .context("creating editorconfig parser")?;
 249        let is_root = parser.is_root;
 250        let sections = parser
 251            .collect::<Result<SmallVec<_>, _>>()
 252            .context("parsing editorconfig sections")?;
 253        Ok(Self { is_root, sections })
 254    }
 255}
 256
 257#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 258pub enum LocalSettingsKind {
 259    Settings,
 260    Tasks,
 261    Editorconfig,
 262    Debug,
 263}
 264
 265impl Global for SettingsStore {}
 266
 267#[derive(Debug)]
 268struct SettingValue<T> {
 269    global_value: Option<T>,
 270    local_values: Vec<(WorktreeId, Arc<Path>, T)>,
 271}
 272
 273trait AnySettingValue: 'static + Send + Sync {
 274    fn key(&self) -> Option<&'static str>;
 275    fn setting_type_name(&self) -> &'static str;
 276    fn deserialize_setting(&self, json: &Value) -> Result<DeserializedSetting> {
 277        self.deserialize_setting_with_key(json).1
 278    }
 279    fn deserialize_setting_with_key(
 280        &self,
 281        json: &Value,
 282    ) -> (Option<&'static str>, Result<DeserializedSetting>);
 283    fn load_setting(
 284        &self,
 285        sources: SettingsSources<DeserializedSetting>,
 286        cx: &mut App,
 287    ) -> Result<Box<dyn Any>>;
 288    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
 289    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>;
 290    fn set_global_value(&mut self, value: Box<dyn Any>);
 291    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>);
 292    fn json_schema(&self, generator: &mut schemars::SchemaGenerator) -> schemars::Schema;
 293    fn edits_for_update(
 294        &self,
 295        raw_settings: &serde_json::Value,
 296        tab_size: usize,
 297        vscode_settings: &VsCodeSettings,
 298        text: &mut String,
 299        edits: &mut Vec<(Range<usize>, String)>,
 300    );
 301    fn settings_ui_item(&self) -> SettingsUiEntry;
 302}
 303
 304struct DeserializedSetting(Box<dyn Any>);
 305
 306impl SettingsStore {
 307    pub fn new(cx: &App) -> Self {
 308        let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
 309        Self {
 310            setting_values: Default::default(),
 311            raw_default_settings: json!({}),
 312            raw_global_settings: None,
 313            raw_user_settings: json!({}),
 314            raw_server_settings: None,
 315            raw_extension_settings: json!({}),
 316            raw_local_settings: Default::default(),
 317            raw_editorconfig_settings: BTreeMap::default(),
 318            tab_size_callback: Default::default(),
 319            setting_file_updates_tx,
 320            _setting_file_updates: cx.spawn(async move |cx| {
 321                while let Some(setting_file_update) = setting_file_updates_rx.next().await {
 322                    (setting_file_update)(cx.clone()).await.log_err();
 323                }
 324            }),
 325        }
 326    }
 327
 328    pub fn observe_active_settings_profile_name(cx: &mut App) -> gpui::Subscription {
 329        cx.observe_global::<ActiveSettingsProfileName>(|cx| {
 330            Self::update_global(cx, |store, cx| {
 331                store.recompute_values(None, cx).log_err();
 332            });
 333        })
 334    }
 335
 336    pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R
 337    where
 338        C: BorrowAppContext,
 339    {
 340        cx.update_global(f)
 341    }
 342
 343    /// Add a new type of setting to the store.
 344    pub fn register_setting<T: Settings>(&mut self, cx: &mut App) {
 345        let setting_type_id = TypeId::of::<T>();
 346        let entry = self.setting_values.entry(setting_type_id);
 347
 348        if matches!(entry, hash_map::Entry::Occupied(_)) {
 349            return;
 350        }
 351
 352        let setting_value = entry.or_insert(Box::new(SettingValue::<T> {
 353            global_value: None,
 354            local_values: Vec::new(),
 355        }));
 356
 357        if let Some(default_settings) = setting_value
 358            .deserialize_setting(&self.raw_default_settings)
 359            .log_err()
 360        {
 361            let user_value = setting_value
 362                .deserialize_setting(&self.raw_user_settings)
 363                .log_err();
 364
 365            let mut release_channel_value = None;
 366            if let Some(release_settings) = &self
 367                .raw_user_settings
 368                .get(release_channel::RELEASE_CHANNEL.dev_name())
 369            {
 370                release_channel_value = setting_value
 371                    .deserialize_setting(release_settings)
 372                    .log_err();
 373            }
 374
 375            let mut os_settings_value = None;
 376            if let Some(os_settings) = &self.raw_user_settings.get(env::consts::OS) {
 377                os_settings_value = setting_value.deserialize_setting(os_settings).log_err();
 378            }
 379
 380            let mut profile_value = None;
 381            if let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>()
 382                && let Some(profiles) = self.raw_user_settings.get("profiles")
 383                && let Some(profile_settings) = profiles.get(&active_profile.0)
 384            {
 385                profile_value = setting_value
 386                    .deserialize_setting(profile_settings)
 387                    .log_err();
 388            }
 389
 390            let server_value = self
 391                .raw_server_settings
 392                .as_ref()
 393                .and_then(|server_setting| {
 394                    setting_value.deserialize_setting(server_setting).log_err()
 395                });
 396
 397            let extension_value = setting_value
 398                .deserialize_setting(&self.raw_extension_settings)
 399                .log_err();
 400
 401            if let Some(setting) = setting_value
 402                .load_setting(
 403                    SettingsSources {
 404                        default: &default_settings,
 405                        global: None,
 406                        extensions: extension_value.as_ref(),
 407                        user: user_value.as_ref(),
 408                        release_channel: release_channel_value.as_ref(),
 409                        operating_system: os_settings_value.as_ref(),
 410                        profile: profile_value.as_ref(),
 411                        server: server_value.as_ref(),
 412                        project: &[],
 413                    },
 414                    cx,
 415                )
 416                .context("A default setting must be added to the `default.json` file")
 417                .log_err()
 418            {
 419                setting_value.set_global_value(setting);
 420            }
 421        }
 422    }
 423
 424    /// Get the value of a setting.
 425    ///
 426    /// Panics if the given setting type has not been registered, or if there is no
 427    /// value for this setting.
 428    pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T {
 429        self.setting_values
 430            .get(&TypeId::of::<T>())
 431            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 432            .value_for_path(path)
 433            .downcast_ref::<T>()
 434            .expect("no default value for setting type")
 435    }
 436
 437    /// Get the value of a setting.
 438    ///
 439    /// Does not panic
 440    pub fn try_get<T: Settings>(&self, path: Option<SettingsLocation>) -> Option<&T> {
 441        self.setting_values
 442            .get(&TypeId::of::<T>())
 443            .map(|value| value.value_for_path(path))
 444            .and_then(|value| value.downcast_ref::<T>())
 445    }
 446
 447    /// Get all values from project specific settings
 448    pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<Path>, &T)> {
 449        self.setting_values
 450            .get(&TypeId::of::<T>())
 451            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 452            .all_local_values()
 453            .into_iter()
 454            .map(|(id, path, any)| {
 455                (
 456                    id,
 457                    path,
 458                    any.downcast_ref::<T>()
 459                        .expect("wrong value type for setting"),
 460                )
 461            })
 462            .collect()
 463    }
 464
 465    /// Override the global value for a setting.
 466    ///
 467    /// The given value will be overwritten if the user settings file changes.
 468    pub fn override_global<T: Settings>(&mut self, value: T) {
 469        self.setting_values
 470            .get_mut(&TypeId::of::<T>())
 471            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 472            .set_global_value(Box::new(value))
 473    }
 474
 475    /// Get the user's settings as a raw JSON value.
 476    ///
 477    /// For user-facing functionality use the typed setting interface.
 478    /// (e.g. ProjectSettings::get_global(cx))
 479    pub fn raw_user_settings(&self) -> &Value {
 480        &self.raw_user_settings
 481    }
 482
 483    /// Replaces current settings with the values from the given JSON.
 484    pub fn set_raw_user_settings(&mut self, new_settings: Value, cx: &mut App) -> Result<()> {
 485        self.raw_user_settings = new_settings;
 486        self.recompute_values(None, cx)?;
 487        Ok(())
 488    }
 489
 490    /// Replaces current settings with the values from the given JSON.
 491    pub fn set_raw_server_settings(
 492        &mut self,
 493        new_settings: Option<Value>,
 494        cx: &mut App,
 495    ) -> Result<()> {
 496        self.raw_server_settings = new_settings;
 497        self.recompute_values(None, cx)?;
 498        Ok(())
 499    }
 500
 501    /// Get the configured settings profile names.
 502    pub fn configured_settings_profiles(&self) -> impl Iterator<Item = &str> {
 503        self.raw_user_settings
 504            .get("profiles")
 505            .and_then(|v| v.as_object())
 506            .into_iter()
 507            .flat_map(|obj| obj.keys())
 508            .map(|s| s.as_str())
 509    }
 510
 511    /// Access the raw JSON value of the global settings.
 512    pub fn raw_global_settings(&self) -> Option<&Value> {
 513        self.raw_global_settings.as_ref()
 514    }
 515
 516    /// Access the raw JSON value of the default settings.
 517    pub fn raw_default_settings(&self) -> &Value {
 518        &self.raw_default_settings
 519    }
 520
 521    #[cfg(any(test, feature = "test-support"))]
 522    pub fn test(cx: &mut App) -> Self {
 523        let mut this = Self::new(cx);
 524        this.set_default_settings(&crate::test_settings(), cx)
 525            .unwrap();
 526        this.set_user_settings("{}", cx).unwrap();
 527        this
 528    }
 529
 530    /// Updates the value of a setting in the user's global configuration.
 531    ///
 532    /// This is only for tests. Normally, settings are only loaded from
 533    /// JSON files.
 534    #[cfg(any(test, feature = "test-support"))]
 535    pub fn update_user_settings<T: Settings>(
 536        &mut self,
 537        cx: &mut App,
 538        update: impl FnOnce(&mut T::FileContent),
 539    ) {
 540        let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();
 541        let new_text = self.new_text_for_update::<T>(old_text, update);
 542        self.set_user_settings(&new_text, cx).unwrap();
 543    }
 544
 545    pub async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {
 546        match fs.load(paths::settings_file()).await {
 547            result @ Ok(_) => result,
 548            Err(err) => {
 549                if let Some(e) = err.downcast_ref::<std::io::Error>()
 550                    && e.kind() == std::io::ErrorKind::NotFound
 551                {
 552                    return Ok(crate::initial_user_settings_content().to_string());
 553                }
 554                Err(err)
 555            }
 556        }
 557    }
 558
 559    fn update_settings_file_inner(
 560        &self,
 561        fs: Arc<dyn Fs>,
 562        update: impl 'static + Send + FnOnce(String, AsyncApp) -> Result<String>,
 563    ) -> oneshot::Receiver<Result<()>> {
 564        let (tx, rx) = oneshot::channel::<Result<()>>();
 565        self.setting_file_updates_tx
 566            .unbounded_send(Box::new(move |cx: AsyncApp| {
 567                async move {
 568                    let res = async move {
 569                        let old_text = Self::load_settings(&fs).await?;
 570                        let new_text = update(old_text, cx)?;
 571                        let settings_path = paths::settings_file().as_path();
 572                        if fs.is_file(settings_path).await {
 573                            let resolved_path =
 574                                fs.canonicalize(settings_path).await.with_context(|| {
 575                                    format!(
 576                                        "Failed to canonicalize settings path {:?}",
 577                                        settings_path
 578                                    )
 579                                })?;
 580
 581                            fs.atomic_write(resolved_path.clone(), new_text)
 582                                .await
 583                                .with_context(|| {
 584                                    format!("Failed to write settings to file {:?}", resolved_path)
 585                                })?;
 586                        } else {
 587                            fs.atomic_write(settings_path.to_path_buf(), new_text)
 588                                .await
 589                                .with_context(|| {
 590                                    format!("Failed to write settings to file {:?}", settings_path)
 591                                })?;
 592                        }
 593                        anyhow::Ok(())
 594                    }
 595                    .await;
 596
 597                    let new_res = match &res {
 598                        Ok(_) => anyhow::Ok(()),
 599                        Err(e) => Err(anyhow::anyhow!("Failed to write settings to file {:?}", e)),
 600                    };
 601
 602                    _ = tx.send(new_res);
 603                    res
 604                }
 605                .boxed_local()
 606            }))
 607            .map_err(|err| anyhow::format_err!("Failed to update settings file: {}", err))
 608            .log_with_level(log::Level::Warn);
 609        return rx;
 610    }
 611
 612    pub fn update_settings_file_at_path(
 613        &self,
 614        fs: Arc<dyn Fs>,
 615        path: &[impl AsRef<str>],
 616        new_value: serde_json::Value,
 617    ) -> oneshot::Receiver<Result<()>> {
 618        let key_path = path
 619            .into_iter()
 620            .map(AsRef::as_ref)
 621            .map(SharedString::new)
 622            .collect::<Vec<_>>();
 623        let update = move |mut old_text: String, cx: AsyncApp| {
 624            cx.read_global(|store: &SettingsStore, _cx| {
 625                // todo(settings_ui) use `update_value_in_json_text` for merging new and old objects with comment preservation, needs old value though...
 626                let (range, replacement) = replace_value_in_json_text(
 627                    &old_text,
 628                    key_path.as_slice(),
 629                    store.json_tab_size(),
 630                    Some(&new_value),
 631                    None,
 632                );
 633                old_text.replace_range(range, &replacement);
 634                old_text
 635            })
 636        };
 637        self.update_settings_file_inner(fs, update)
 638    }
 639
 640    pub fn update_settings_file<T: Settings>(
 641        &self,
 642        fs: Arc<dyn Fs>,
 643        update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
 644    ) {
 645        _ = self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| {
 646            cx.read_global(|store: &SettingsStore, cx| {
 647                store.new_text_for_update::<T>(old_text, |content| update(content, cx))
 648            })
 649        });
 650    }
 651
 652    pub fn import_vscode_settings(
 653        &self,
 654        fs: Arc<dyn Fs>,
 655        vscode_settings: VsCodeSettings,
 656    ) -> oneshot::Receiver<Result<()>> {
 657        self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| {
 658            cx.read_global(|store: &SettingsStore, _cx| {
 659                store.get_vscode_edits(old_text, &vscode_settings)
 660            })
 661        })
 662    }
 663
 664    pub fn settings_ui_items(&self) -> impl IntoIterator<Item = SettingsUiEntry> {
 665        self.setting_values
 666            .values()
 667            .map(|item| item.settings_ui_item())
 668    }
 669}
 670
 671impl SettingsStore {
 672    /// Updates the value of a setting in a JSON file, returning the new text
 673    /// for that JSON file.
 674    pub fn new_text_for_update<T: Settings>(
 675        &self,
 676        old_text: String,
 677        update: impl FnOnce(&mut T::FileContent),
 678    ) -> String {
 679        let edits = self.edits_for_update::<T>(&old_text, update);
 680        let mut new_text = old_text;
 681        for (range, replacement) in edits.into_iter() {
 682            new_text.replace_range(range, &replacement);
 683        }
 684        new_text
 685    }
 686
 687    pub fn get_vscode_edits(&self, mut old_text: String, vscode: &VsCodeSettings) -> String {
 688        let mut new_text = old_text.clone();
 689        let mut edits: Vec<(Range<usize>, String)> = Vec::new();
 690        let raw_settings = parse_json_with_comments::<Value>(&old_text).unwrap_or_default();
 691        let tab_size = self.json_tab_size();
 692        for v in self.setting_values.values() {
 693            v.edits_for_update(&raw_settings, tab_size, vscode, &mut old_text, &mut edits);
 694        }
 695        for (range, replacement) in edits.into_iter() {
 696            new_text.replace_range(range, &replacement);
 697        }
 698        new_text
 699    }
 700
 701    /// Updates the value of a setting in a JSON file, returning a list
 702    /// of edits to apply to the JSON file.
 703    pub fn edits_for_update<T: Settings>(
 704        &self,
 705        text: &str,
 706        update: impl FnOnce(&mut T::FileContent),
 707    ) -> Vec<(Range<usize>, String)> {
 708        let setting_type_id = TypeId::of::<T>();
 709
 710        let preserved_keys = T::PRESERVED_KEYS.unwrap_or_default();
 711
 712        let setting = self
 713            .setting_values
 714            .get(&setting_type_id)
 715            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()));
 716        let raw_settings = parse_json_with_comments::<Value>(text).unwrap_or_default();
 717        let (key, deserialized_setting) = setting.deserialize_setting_with_key(&raw_settings);
 718        let old_content = match deserialized_setting {
 719            Ok(content) => content.0.downcast::<T::FileContent>().unwrap(),
 720            Err(_) => Box::<<T as Settings>::FileContent>::default(),
 721        };
 722        let mut new_content = old_content.clone();
 723        update(&mut new_content);
 724
 725        let old_value = serde_json::to_value(&old_content).unwrap();
 726        let new_value = serde_json::to_value(new_content).unwrap();
 727
 728        let mut key_path = Vec::new();
 729        if let Some(key) = key {
 730            key_path.push(key);
 731        }
 732
 733        let mut edits = Vec::new();
 734        let tab_size = self.json_tab_size();
 735        let mut text = text.to_string();
 736        update_value_in_json_text(
 737            &mut text,
 738            &mut key_path,
 739            tab_size,
 740            &old_value,
 741            &new_value,
 742            preserved_keys,
 743            &mut edits,
 744        );
 745        edits
 746    }
 747
 748    /// Configure the tab sized when updating JSON files.
 749    pub fn set_json_tab_size_callback<T: Settings>(
 750        &mut self,
 751        get_tab_size: fn(&T) -> Option<usize>,
 752    ) {
 753        self.tab_size_callback = Some((
 754            TypeId::of::<T>(),
 755            Box::new(move |value| get_tab_size(value.downcast_ref::<T>().unwrap())),
 756        ));
 757    }
 758
 759    pub fn json_tab_size(&self) -> usize {
 760        const DEFAULT_JSON_TAB_SIZE: usize = 2;
 761
 762        if let Some((setting_type_id, callback)) = &self.tab_size_callback {
 763            let setting_value = self.setting_values.get(setting_type_id).unwrap();
 764            let value = setting_value.value_for_path(None);
 765            if let Some(value) = callback(value) {
 766                return value;
 767            }
 768        }
 769
 770        DEFAULT_JSON_TAB_SIZE
 771    }
 772
 773    /// Sets the default settings via a JSON string.
 774    ///
 775    /// The string should contain a JSON object with a default value for every setting.
 776    pub fn set_default_settings(
 777        &mut self,
 778        default_settings_content: &str,
 779        cx: &mut App,
 780    ) -> Result<()> {
 781        let settings: Value = parse_json_with_comments(default_settings_content)?;
 782        anyhow::ensure!(settings.is_object(), "settings must be an object");
 783        self.raw_default_settings = settings;
 784        self.recompute_values(None, cx)?;
 785        Ok(())
 786    }
 787
 788    /// Sets the user settings via a JSON string.
 789    pub fn set_user_settings(
 790        &mut self,
 791        user_settings_content: &str,
 792        cx: &mut App,
 793    ) -> Result<Value> {
 794        let settings: Value = if user_settings_content.is_empty() {
 795            parse_json_with_comments("{}")?
 796        } else {
 797            parse_json_with_comments(user_settings_content)?
 798        };
 799
 800        anyhow::ensure!(settings.is_object(), "settings must be an object");
 801        self.raw_user_settings = settings.clone();
 802        self.recompute_values(None, cx)?;
 803        Ok(settings)
 804    }
 805
 806    /// Sets the global settings via a JSON string.
 807    pub fn set_global_settings(
 808        &mut self,
 809        global_settings_content: &str,
 810        cx: &mut App,
 811    ) -> Result<Value> {
 812        let settings: Value = if global_settings_content.is_empty() {
 813            parse_json_with_comments("{}")?
 814        } else {
 815            parse_json_with_comments(global_settings_content)?
 816        };
 817
 818        anyhow::ensure!(settings.is_object(), "settings must be an object");
 819        self.raw_global_settings = Some(settings.clone());
 820        self.recompute_values(None, cx)?;
 821        Ok(settings)
 822    }
 823
 824    pub fn set_server_settings(
 825        &mut self,
 826        server_settings_content: &str,
 827        cx: &mut App,
 828    ) -> Result<()> {
 829        let settings: Option<Value> = if server_settings_content.is_empty() {
 830            None
 831        } else {
 832            parse_json_with_comments(server_settings_content)?
 833        };
 834
 835        anyhow::ensure!(
 836            settings
 837                .as_ref()
 838                .map(|value| value.is_object())
 839                .unwrap_or(true),
 840            "settings must be an object"
 841        );
 842        self.raw_server_settings = settings;
 843        self.recompute_values(None, cx)?;
 844        Ok(())
 845    }
 846
 847    /// Add or remove a set of local settings via a JSON string.
 848    pub fn set_local_settings(
 849        &mut self,
 850        root_id: WorktreeId,
 851        directory_path: Arc<Path>,
 852        kind: LocalSettingsKind,
 853        settings_content: Option<&str>,
 854        cx: &mut App,
 855    ) -> std::result::Result<(), InvalidSettingsError> {
 856        let mut zed_settings_changed = false;
 857        match (
 858            kind,
 859            settings_content
 860                .map(|content| content.trim())
 861                .filter(|content| !content.is_empty()),
 862        ) {
 863            (LocalSettingsKind::Tasks, _) => {
 864                return Err(InvalidSettingsError::Tasks {
 865                    message: "Attempted to submit tasks into the settings store".to_string(),
 866                    path: directory_path.join(task_file_name()),
 867                });
 868            }
 869            (LocalSettingsKind::Debug, _) => {
 870                return Err(InvalidSettingsError::Debug {
 871                    message: "Attempted to submit debugger config into the settings store"
 872                        .to_string(),
 873                    path: directory_path.join(task_file_name()),
 874                });
 875            }
 876            (LocalSettingsKind::Settings, None) => {
 877                zed_settings_changed = self
 878                    .raw_local_settings
 879                    .remove(&(root_id, directory_path.clone()))
 880                    .is_some()
 881            }
 882            (LocalSettingsKind::Editorconfig, None) => {
 883                self.raw_editorconfig_settings
 884                    .remove(&(root_id, directory_path.clone()));
 885            }
 886            (LocalSettingsKind::Settings, Some(settings_contents)) => {
 887                let new_settings =
 888                    parse_json_with_comments::<Value>(settings_contents).map_err(|e| {
 889                        InvalidSettingsError::LocalSettings {
 890                            path: directory_path.join(local_settings_file_relative_path()),
 891                            message: e.to_string(),
 892                        }
 893                    })?;
 894                match self
 895                    .raw_local_settings
 896                    .entry((root_id, directory_path.clone()))
 897                {
 898                    btree_map::Entry::Vacant(v) => {
 899                        v.insert(new_settings);
 900                        zed_settings_changed = true;
 901                    }
 902                    btree_map::Entry::Occupied(mut o) => {
 903                        if o.get() != &new_settings {
 904                            o.insert(new_settings);
 905                            zed_settings_changed = true;
 906                        }
 907                    }
 908                }
 909            }
 910            (LocalSettingsKind::Editorconfig, Some(editorconfig_contents)) => {
 911                match self
 912                    .raw_editorconfig_settings
 913                    .entry((root_id, directory_path.clone()))
 914                {
 915                    btree_map::Entry::Vacant(v) => match editorconfig_contents.parse() {
 916                        Ok(new_contents) => {
 917                            v.insert((editorconfig_contents.to_owned(), Some(new_contents)));
 918                        }
 919                        Err(e) => {
 920                            v.insert((editorconfig_contents.to_owned(), None));
 921                            return Err(InvalidSettingsError::Editorconfig {
 922                                message: e.to_string(),
 923                                path: directory_path.join(EDITORCONFIG_NAME),
 924                            });
 925                        }
 926                    },
 927                    btree_map::Entry::Occupied(mut o) => {
 928                        if o.get().0 != editorconfig_contents {
 929                            match editorconfig_contents.parse() {
 930                                Ok(new_contents) => {
 931                                    o.insert((
 932                                        editorconfig_contents.to_owned(),
 933                                        Some(new_contents),
 934                                    ));
 935                                }
 936                                Err(e) => {
 937                                    o.insert((editorconfig_contents.to_owned(), None));
 938                                    return Err(InvalidSettingsError::Editorconfig {
 939                                        message: e.to_string(),
 940                                        path: directory_path.join(EDITORCONFIG_NAME),
 941                                    });
 942                                }
 943                            }
 944                        }
 945                    }
 946                }
 947            }
 948        };
 949
 950        if zed_settings_changed {
 951            self.recompute_values(Some((root_id, &directory_path)), cx)?;
 952        }
 953        Ok(())
 954    }
 955
 956    pub fn set_extension_settings<T: Serialize>(&mut self, content: T, cx: &mut App) -> Result<()> {
 957        let settings: Value = serde_json::to_value(content)?;
 958        anyhow::ensure!(settings.is_object(), "settings must be an object");
 959        self.raw_extension_settings = settings;
 960        self.recompute_values(None, cx)?;
 961        Ok(())
 962    }
 963
 964    /// Add or remove a set of local settings via a JSON string.
 965    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
 966        self.raw_local_settings
 967            .retain(|(worktree_id, _), _| worktree_id != &root_id);
 968        self.recompute_values(Some((root_id, "".as_ref())), cx)?;
 969        Ok(())
 970    }
 971
 972    pub fn local_settings(
 973        &self,
 974        root_id: WorktreeId,
 975    ) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
 976        self.raw_local_settings
 977            .range(
 978                (root_id, Path::new("").into())
 979                    ..(
 980                        WorktreeId::from_usize(root_id.to_usize() + 1),
 981                        Path::new("").into(),
 982                    ),
 983            )
 984            .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))
 985    }
 986
 987    pub fn local_editorconfig_settings(
 988        &self,
 989        root_id: WorktreeId,
 990    ) -> impl '_ + Iterator<Item = (Arc<Path>, String, Option<Editorconfig>)> {
 991        self.raw_editorconfig_settings
 992            .range(
 993                (root_id, Path::new("").into())
 994                    ..(
 995                        WorktreeId::from_usize(root_id.to_usize() + 1),
 996                        Path::new("").into(),
 997                    ),
 998            )
 999            .map(|((_, path), (content, parsed_content))| {
1000                (path.clone(), content.clone(), parsed_content.clone())
1001            })
1002    }
1003
1004    pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams, cx: &App) -> Value {
1005        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
1006            .with_transform(DefaultDenyUnknownFields)
1007            .into_generator();
1008        let mut combined_schema = json!({
1009            "type": "object",
1010            "properties": {}
1011        });
1012
1013        // Merge together settings schemas, similarly to json schema's "allOf". This merging is
1014        // recursive, though at time of writing this recursive nature isn't used very much. An
1015        // example of it is the schema for `jupyter` having contribution from both `EditorSettings`
1016        // and `JupyterSettings`.
1017        //
1018        // This logic could be removed in favor of "allOf", but then there isn't the opportunity to
1019        // validate and fully control the merge.
1020        for setting_value in self.setting_values.values() {
1021            let mut setting_schema = setting_value.json_schema(&mut generator);
1022
1023            if let Some(key) = setting_value.key() {
1024                if let Some(properties) = combined_schema.get_mut("properties")
1025                    && let Some(properties_obj) = properties.as_object_mut()
1026                {
1027                    if let Some(target) = properties_obj.get_mut(key) {
1028                        merge_schema(target, setting_schema.to_value());
1029                    } else {
1030                        properties_obj.insert(key.to_string(), setting_schema.to_value());
1031                    }
1032                }
1033            } else {
1034                setting_schema.remove("description");
1035                setting_schema.remove("additionalProperties");
1036                merge_schema(&mut combined_schema, setting_schema.to_value());
1037            }
1038        }
1039
1040        fn merge_schema(target: &mut serde_json::Value, source: serde_json::Value) {
1041            let (Some(target_obj), serde_json::Value::Object(source_obj)) =
1042                (target.as_object_mut(), source)
1043            else {
1044                return;
1045            };
1046
1047            for (source_key, source_value) in source_obj {
1048                match source_key.as_str() {
1049                    "properties" => {
1050                        let serde_json::Value::Object(source_properties) = source_value else {
1051                            log::error!(
1052                                "bug: expected object for `{}` json schema field, but got: {}",
1053                                source_key,
1054                                source_value
1055                            );
1056                            continue;
1057                        };
1058                        let target_properties =
1059                            target_obj.entry(source_key.clone()).or_insert(json!({}));
1060                        let Some(target_properties) = target_properties.as_object_mut() else {
1061                            log::error!(
1062                                "bug: expected object for `{}` json schema field, but got: {}",
1063                                source_key,
1064                                target_properties
1065                            );
1066                            continue;
1067                        };
1068                        for (key, value) in source_properties {
1069                            if let Some(existing) = target_properties.get_mut(&key) {
1070                                merge_schema(existing, value);
1071                            } else {
1072                                target_properties.insert(key, value);
1073                            }
1074                        }
1075                    }
1076                    "allOf" | "anyOf" | "oneOf" => {
1077                        let serde_json::Value::Array(source_array) = source_value else {
1078                            log::error!(
1079                                "bug: expected array for `{}` json schema field, but got: {}",
1080                                source_key,
1081                                source_value,
1082                            );
1083                            continue;
1084                        };
1085                        let target_array =
1086                            target_obj.entry(source_key.clone()).or_insert(json!([]));
1087                        let Some(target_array) = target_array.as_array_mut() else {
1088                            log::error!(
1089                                "bug: expected array for `{}` json schema field, but got: {}",
1090                                source_key,
1091                                target_array,
1092                            );
1093                            continue;
1094                        };
1095                        target_array.extend(source_array);
1096                    }
1097                    "type"
1098                    | "$ref"
1099                    | "enum"
1100                    | "minimum"
1101                    | "maximum"
1102                    | "pattern"
1103                    | "description"
1104                    | "additionalProperties" => {
1105                        if let Some(old_value) =
1106                            target_obj.insert(source_key.clone(), source_value.clone())
1107                            && old_value != source_value
1108                        {
1109                            log::error!(
1110                                "bug: while merging JSON schemas, \
1111                                    mismatch `\"{}\": {}` (before was `{}`)",
1112                                source_key,
1113                                old_value,
1114                                source_value
1115                            );
1116                        }
1117                    }
1118                    _ => {
1119                        log::error!(
1120                            "bug: while merging settings JSON schemas, \
1121                            encountered unexpected `\"{}\": {}`",
1122                            source_key,
1123                            source_value
1124                        );
1125                    }
1126                }
1127            }
1128        }
1129
1130        // add schemas which are determined at runtime
1131        for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
1132            (parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
1133        }
1134
1135        // add merged settings schema to the definitions
1136        const ZED_SETTINGS: &str = "ZedSettings";
1137        let zed_settings_ref = add_new_subschema(&mut generator, ZED_SETTINGS, combined_schema);
1138
1139        // add `ZedSettingsOverride` which is the same as `ZedSettings` except that unknown
1140        // fields are rejected. This is used for release stage settings and profiles.
1141        let mut zed_settings_override = zed_settings_ref.clone();
1142        zed_settings_override.insert("unevaluatedProperties".to_string(), false.into());
1143        let zed_settings_override_ref = add_new_subschema(
1144            &mut generator,
1145            "ZedSettingsOverride",
1146            zed_settings_override.to_value(),
1147        );
1148
1149        // Remove `"additionalProperties": false` added by `DefaultDenyUnknownFields` so that
1150        // unknown fields can be handled by the root schema and `ZedSettingsOverride`.
1151        let mut definitions = generator.take_definitions(true);
1152        definitions
1153            .get_mut(ZED_SETTINGS)
1154            .unwrap()
1155            .as_object_mut()
1156            .unwrap()
1157            .remove("additionalProperties");
1158
1159        let meta_schema = generator
1160            .settings()
1161            .meta_schema
1162            .as_ref()
1163            .expect("meta_schema should be present in schemars settings")
1164            .to_string();
1165
1166        json!({
1167            "$schema": meta_schema,
1168            "title": "Zed Settings",
1169            "unevaluatedProperties": false,
1170            // ZedSettings + settings overrides for each release stage / OS / profiles
1171            "allOf": [
1172                zed_settings_ref,
1173                {
1174                    "properties": {
1175                        "dev": zed_settings_override_ref,
1176                        "nightly": zed_settings_override_ref,
1177                        "stable": zed_settings_override_ref,
1178                        "preview": zed_settings_override_ref,
1179                        "linux": zed_settings_override_ref,
1180                        "macos": zed_settings_override_ref,
1181                        "windows": zed_settings_override_ref,
1182                        "profiles": {
1183                            "type": "object",
1184                            "description": "Configures any number of settings profiles.",
1185                            "additionalProperties": zed_settings_override_ref
1186                        }
1187                    }
1188                }
1189            ],
1190            "$defs": definitions,
1191        })
1192    }
1193
1194    fn recompute_values(
1195        &mut self,
1196        changed_local_path: Option<(WorktreeId, &Path)>,
1197        cx: &mut App,
1198    ) -> std::result::Result<(), InvalidSettingsError> {
1199        // Reload the global and local values for every setting.
1200        let mut project_settings_stack = Vec::<DeserializedSetting>::new();
1201        let mut paths_stack = Vec::<Option<(WorktreeId, &Path)>>::new();
1202        for setting_value in self.setting_values.values_mut() {
1203            let default_settings = setting_value
1204                .deserialize_setting(&self.raw_default_settings)
1205                .map_err(|e| InvalidSettingsError::DefaultSettings {
1206                    message: e.to_string(),
1207                })?;
1208
1209            let global_settings = self
1210                .raw_global_settings
1211                .as_ref()
1212                .and_then(|setting| setting_value.deserialize_setting(setting).log_err());
1213
1214            let extension_settings = setting_value
1215                .deserialize_setting(&self.raw_extension_settings)
1216                .log_err();
1217
1218            let user_settings = match setting_value.deserialize_setting(&self.raw_user_settings) {
1219                Ok(settings) => Some(settings),
1220                Err(error) => {
1221                    return Err(InvalidSettingsError::UserSettings {
1222                        message: error.to_string(),
1223                    });
1224                }
1225            };
1226
1227            let server_settings = self
1228                .raw_server_settings
1229                .as_ref()
1230                .and_then(|setting| setting_value.deserialize_setting(setting).log_err());
1231
1232            let mut release_channel_settings = None;
1233            if let Some(release_settings) = &self
1234                .raw_user_settings
1235                .get(release_channel::RELEASE_CHANNEL.dev_name())
1236                && let Some(release_settings) = setting_value
1237                    .deserialize_setting(release_settings)
1238                    .log_err()
1239            {
1240                release_channel_settings = Some(release_settings);
1241            }
1242
1243            let mut os_settings = None;
1244            if let Some(settings) = &self.raw_user_settings.get(env::consts::OS)
1245                && let Some(settings) = setting_value.deserialize_setting(settings).log_err()
1246            {
1247                os_settings = Some(settings);
1248            }
1249
1250            let mut profile_settings = None;
1251            if let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>()
1252                && let Some(profiles) = self.raw_user_settings.get("profiles")
1253                && let Some(profile_json) = profiles.get(&active_profile.0)
1254            {
1255                profile_settings = setting_value.deserialize_setting(profile_json).log_err();
1256            }
1257
1258            // If the global settings file changed, reload the global value for the field.
1259            if changed_local_path.is_none()
1260                && let Some(value) = setting_value
1261                    .load_setting(
1262                        SettingsSources {
1263                            default: &default_settings,
1264                            global: global_settings.as_ref(),
1265                            extensions: extension_settings.as_ref(),
1266                            user: user_settings.as_ref(),
1267                            release_channel: release_channel_settings.as_ref(),
1268                            operating_system: os_settings.as_ref(),
1269                            profile: profile_settings.as_ref(),
1270                            server: server_settings.as_ref(),
1271                            project: &[],
1272                        },
1273                        cx,
1274                    )
1275                    .log_err()
1276            {
1277                setting_value.set_global_value(value);
1278            }
1279
1280            // Reload the local values for the setting.
1281            paths_stack.clear();
1282            project_settings_stack.clear();
1283            for ((root_id, directory_path), local_settings) in &self.raw_local_settings {
1284                // Build a stack of all of the local values for that setting.
1285                while let Some(prev_entry) = paths_stack.last() {
1286                    if let Some((prev_root_id, prev_path)) = prev_entry
1287                        && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
1288                    {
1289                        paths_stack.pop();
1290                        project_settings_stack.pop();
1291                        continue;
1292                    }
1293                    break;
1294                }
1295
1296                match setting_value.deserialize_setting(local_settings) {
1297                    Ok(local_settings) => {
1298                        paths_stack.push(Some((*root_id, directory_path.as_ref())));
1299                        project_settings_stack.push(local_settings);
1300
1301                        // If a local settings file changed, then avoid recomputing local
1302                        // settings for any path outside of that directory.
1303                        if changed_local_path.is_some_and(
1304                            |(changed_root_id, changed_local_path)| {
1305                                *root_id != changed_root_id
1306                                    || !directory_path.starts_with(changed_local_path)
1307                            },
1308                        ) {
1309                            continue;
1310                        }
1311
1312                        if let Some(value) = setting_value
1313                            .load_setting(
1314                                SettingsSources {
1315                                    default: &default_settings,
1316                                    global: global_settings.as_ref(),
1317                                    extensions: extension_settings.as_ref(),
1318                                    user: user_settings.as_ref(),
1319                                    release_channel: release_channel_settings.as_ref(),
1320                                    operating_system: os_settings.as_ref(),
1321                                    profile: profile_settings.as_ref(),
1322                                    server: server_settings.as_ref(),
1323                                    project: &project_settings_stack.iter().collect::<Vec<_>>(),
1324                                },
1325                                cx,
1326                            )
1327                            .log_err()
1328                        {
1329                            setting_value.set_local_value(*root_id, directory_path.clone(), value);
1330                        }
1331                    }
1332                    Err(error) => {
1333                        return Err(InvalidSettingsError::LocalSettings {
1334                            path: directory_path.join(local_settings_file_relative_path()),
1335                            message: error.to_string(),
1336                        });
1337                    }
1338                }
1339            }
1340        }
1341        Ok(())
1342    }
1343
1344    pub fn editorconfig_properties(
1345        &self,
1346        for_worktree: WorktreeId,
1347        for_path: &Path,
1348    ) -> Option<EditorconfigProperties> {
1349        let mut properties = EditorconfigProperties::new();
1350
1351        for (directory_with_config, _, parsed_editorconfig) in
1352            self.local_editorconfig_settings(for_worktree)
1353        {
1354            if !for_path.starts_with(&directory_with_config) {
1355                properties.use_fallbacks();
1356                return Some(properties);
1357            }
1358            let parsed_editorconfig = parsed_editorconfig?;
1359            if parsed_editorconfig.is_root {
1360                properties = EditorconfigProperties::new();
1361            }
1362            for section in parsed_editorconfig.sections {
1363                section.apply_to(&mut properties, for_path).log_err()?;
1364            }
1365        }
1366
1367        properties.use_fallbacks();
1368        Some(properties)
1369    }
1370}
1371
1372#[derive(Debug, Clone, PartialEq)]
1373pub enum InvalidSettingsError {
1374    LocalSettings { path: PathBuf, message: String },
1375    UserSettings { message: String },
1376    ServerSettings { message: String },
1377    DefaultSettings { message: String },
1378    Editorconfig { path: PathBuf, message: String },
1379    Tasks { path: PathBuf, message: String },
1380    Debug { path: PathBuf, message: String },
1381}
1382
1383impl std::fmt::Display for InvalidSettingsError {
1384    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1385        match self {
1386            InvalidSettingsError::LocalSettings { message, .. }
1387            | InvalidSettingsError::UserSettings { message }
1388            | InvalidSettingsError::ServerSettings { message }
1389            | InvalidSettingsError::DefaultSettings { message }
1390            | InvalidSettingsError::Tasks { message, .. }
1391            | InvalidSettingsError::Editorconfig { message, .. }
1392            | InvalidSettingsError::Debug { message, .. } => {
1393                write!(f, "{message}")
1394            }
1395        }
1396    }
1397}
1398impl std::error::Error for InvalidSettingsError {}
1399
1400impl Debug for SettingsStore {
1401    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1402        f.debug_struct("SettingsStore")
1403            .field(
1404                "types",
1405                &self
1406                    .setting_values
1407                    .values()
1408                    .map(|value| value.setting_type_name())
1409                    .collect::<Vec<_>>(),
1410            )
1411            .field("default_settings", &self.raw_default_settings)
1412            .field("user_settings", &self.raw_user_settings)
1413            .field("local_settings", &self.raw_local_settings)
1414            .finish_non_exhaustive()
1415    }
1416}
1417
1418impl<T: Settings> AnySettingValue for SettingValue<T> {
1419    fn key(&self) -> Option<&'static str> {
1420        T::FileContent::KEY
1421    }
1422
1423    fn setting_type_name(&self) -> &'static str {
1424        type_name::<T>()
1425    }
1426
1427    fn load_setting(
1428        &self,
1429        values: SettingsSources<DeserializedSetting>,
1430        cx: &mut App,
1431    ) -> Result<Box<dyn Any>> {
1432        Ok(Box::new(T::load(
1433            SettingsSources {
1434                default: values.default.0.downcast_ref::<T::FileContent>().unwrap(),
1435                global: values
1436                    .global
1437                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1438                extensions: values
1439                    .extensions
1440                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1441                user: values
1442                    .user
1443                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1444                release_channel: values
1445                    .release_channel
1446                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1447                operating_system: values
1448                    .operating_system
1449                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1450                profile: values
1451                    .profile
1452                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1453                server: values
1454                    .server
1455                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1456                project: values
1457                    .project
1458                    .iter()
1459                    .map(|value| value.0.downcast_ref().unwrap())
1460                    .collect::<SmallVec<[_; 3]>>()
1461                    .as_slice(),
1462            },
1463            cx,
1464        )?))
1465    }
1466
1467    fn deserialize_setting_with_key(
1468        &self,
1469        mut json: &Value,
1470    ) -> (Option<&'static str>, Result<DeserializedSetting>) {
1471        let mut key = None;
1472        if let Some(k) = T::FileContent::KEY {
1473            if let Some(value) = json.get(k) {
1474                json = value;
1475                key = Some(k);
1476            } else if let Some((k, value)) =
1477                T::FileContent::FALLBACK_KEY.and_then(|k| Some((k, json.get(k)?)))
1478            {
1479                json = value;
1480                key = Some(k);
1481            } else {
1482                let value = T::FileContent::default();
1483                return (
1484                    T::FileContent::KEY,
1485                    Ok(DeserializedSetting(Box::new(value))),
1486                );
1487            }
1488        }
1489        let value = serde_path_to_error::deserialize::<_, T::FileContent>(json)
1490            .map(|value| DeserializedSetting(Box::new(value)))
1491            .map_err(|err| {
1492                // construct a path using the key and reported error path if possible.
1493                // Unfortunately, serde_path_to_error does not expose the necessary
1494                // methods and data to simply add the key to the path
1495                let mut path = String::new();
1496                if let Some(key) = key {
1497                    path.push_str(key);
1498                }
1499                let err_path = err.path().to_string();
1500                // when the path is empty, serde_path_to_error stringifies the path as ".",
1501                // when the path is unknown, serde_path_to_error stringifies the path as an empty string
1502                if !err_path.is_empty() && !err_path.starts_with(".") {
1503                    path.push('.');
1504                    path.push_str(&err_path);
1505                }
1506                if path.is_empty() {
1507                    anyhow::Error::from(err.into_inner())
1508                } else {
1509                    anyhow::anyhow!("'{}': {}", err.into_inner(), path)
1510                }
1511            });
1512        (key, value)
1513    }
1514
1515    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)> {
1516        self.local_values
1517            .iter()
1518            .map(|(id, path, value)| (*id, path.clone(), value as _))
1519            .collect()
1520    }
1521
1522    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1523        if let Some(SettingsLocation { worktree_id, path }) = path {
1524            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1525                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1526                    return value;
1527                }
1528            }
1529        }
1530
1531        self.global_value
1532            .as_ref()
1533            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1534    }
1535
1536    fn set_global_value(&mut self, value: Box<dyn Any>) {
1537        self.global_value = Some(*value.downcast().unwrap());
1538    }
1539
1540    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>) {
1541        let value = *value.downcast().unwrap();
1542        match self
1543            .local_values
1544            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1545        {
1546            Ok(ix) => self.local_values[ix].2 = value,
1547            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1548        }
1549    }
1550
1551    fn json_schema(&self, generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
1552        T::FileContent::json_schema(generator)
1553    }
1554
1555    fn edits_for_update(
1556        &self,
1557        raw_settings: &serde_json::Value,
1558        tab_size: usize,
1559        vscode_settings: &VsCodeSettings,
1560        text: &mut String,
1561        edits: &mut Vec<(Range<usize>, String)>,
1562    ) {
1563        let (key, deserialized_setting) = self.deserialize_setting_with_key(raw_settings);
1564        let old_content = match deserialized_setting {
1565            Ok(content) => content.0.downcast::<T::FileContent>().unwrap(),
1566            Err(_) => Box::<<T as Settings>::FileContent>::default(),
1567        };
1568        let mut new_content = old_content.clone();
1569        T::import_from_vscode(vscode_settings, &mut new_content);
1570
1571        let old_value = serde_json::to_value(&old_content).unwrap();
1572        let new_value = serde_json::to_value(new_content).unwrap();
1573
1574        let mut key_path = Vec::new();
1575        if let Some(key) = key {
1576            key_path.push(key);
1577        }
1578
1579        update_value_in_json_text(
1580            text,
1581            &mut key_path,
1582            tab_size,
1583            &old_value,
1584            &new_value,
1585            T::PRESERVED_KEYS.unwrap_or_default(),
1586            edits,
1587        );
1588    }
1589
1590    fn settings_ui_item(&self) -> SettingsUiEntry {
1591        <<T as Settings>::FileContent as SettingsUi>::settings_ui_entry()
1592    }
1593}
1594
1595#[cfg(test)]
1596mod tests {
1597    use crate::VsCodeSettingsSource;
1598
1599    use super::*;
1600    // This is so the SettingsUi macro can still work properly
1601    use crate as settings;
1602    use serde::Deserialize;
1603    use settings_ui_macros::{SettingsKey, SettingsUi};
1604    use unindent::Unindent;
1605
1606    #[gpui::test]
1607    fn test_settings_store_basic(cx: &mut App) {
1608        let mut store = SettingsStore::new(cx);
1609        store.register_setting::<UserSettings>(cx);
1610        store.register_setting::<TurboSetting>(cx);
1611        store.register_setting::<MultiKeySettings>(cx);
1612        store
1613            .set_default_settings(
1614                r#"{
1615                    "turbo": false,
1616                    "user": {
1617                        "name": "John Doe",
1618                        "age": 30,
1619                        "staff": false
1620                    }
1621                }"#,
1622                cx,
1623            )
1624            .unwrap();
1625
1626        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
1627        assert_eq!(
1628            store.get::<UserSettings>(None),
1629            &UserSettings {
1630                name: "John Doe".to_string(),
1631                age: 30,
1632                staff: false,
1633            }
1634        );
1635        assert_eq!(
1636            store.get::<MultiKeySettings>(None),
1637            &MultiKeySettings {
1638                key1: String::new(),
1639                key2: String::new(),
1640            }
1641        );
1642
1643        store
1644            .set_user_settings(
1645                r#"{
1646                    "turbo": true,
1647                    "user": { "age": 31 },
1648                    "key1": "a"
1649                }"#,
1650                cx,
1651            )
1652            .unwrap();
1653
1654        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(true));
1655        assert_eq!(
1656            store.get::<UserSettings>(None),
1657            &UserSettings {
1658                name: "John Doe".to_string(),
1659                age: 31,
1660                staff: false
1661            }
1662        );
1663
1664        store
1665            .set_local_settings(
1666                WorktreeId::from_usize(1),
1667                Path::new("/root1").into(),
1668                LocalSettingsKind::Settings,
1669                Some(r#"{ "user": { "staff": true } }"#),
1670                cx,
1671            )
1672            .unwrap();
1673        store
1674            .set_local_settings(
1675                WorktreeId::from_usize(1),
1676                Path::new("/root1/subdir").into(),
1677                LocalSettingsKind::Settings,
1678                Some(r#"{ "user": { "name": "Jane Doe" } }"#),
1679                cx,
1680            )
1681            .unwrap();
1682
1683        store
1684            .set_local_settings(
1685                WorktreeId::from_usize(1),
1686                Path::new("/root2").into(),
1687                LocalSettingsKind::Settings,
1688                Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
1689                cx,
1690            )
1691            .unwrap();
1692
1693        assert_eq!(
1694            store.get::<UserSettings>(Some(SettingsLocation {
1695                worktree_id: WorktreeId::from_usize(1),
1696                path: Path::new("/root1/something"),
1697            })),
1698            &UserSettings {
1699                name: "John Doe".to_string(),
1700                age: 31,
1701                staff: true
1702            }
1703        );
1704        assert_eq!(
1705            store.get::<UserSettings>(Some(SettingsLocation {
1706                worktree_id: WorktreeId::from_usize(1),
1707                path: Path::new("/root1/subdir/something")
1708            })),
1709            &UserSettings {
1710                name: "Jane Doe".to_string(),
1711                age: 31,
1712                staff: true
1713            }
1714        );
1715        assert_eq!(
1716            store.get::<UserSettings>(Some(SettingsLocation {
1717                worktree_id: WorktreeId::from_usize(1),
1718                path: Path::new("/root2/something")
1719            })),
1720            &UserSettings {
1721                name: "John Doe".to_string(),
1722                age: 42,
1723                staff: false
1724            }
1725        );
1726        assert_eq!(
1727            store.get::<MultiKeySettings>(Some(SettingsLocation {
1728                worktree_id: WorktreeId::from_usize(1),
1729                path: Path::new("/root2/something")
1730            })),
1731            &MultiKeySettings {
1732                key1: "a".to_string(),
1733                key2: "b".to_string(),
1734            }
1735        );
1736    }
1737
1738    #[gpui::test]
1739    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1740        let mut store = SettingsStore::new(cx);
1741        store
1742            .set_default_settings(
1743                r#"{
1744                    "turbo": true,
1745                    "user": {
1746                        "name": "John Doe",
1747                        "age": 30,
1748                        "staff": false
1749                    },
1750                    "key1": "x"
1751                }"#,
1752                cx,
1753            )
1754            .unwrap();
1755        store
1756            .set_user_settings(r#"{ "turbo": false }"#, cx)
1757            .unwrap();
1758        store.register_setting::<UserSettings>(cx);
1759        store.register_setting::<TurboSetting>(cx);
1760
1761        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
1762        assert_eq!(
1763            store.get::<UserSettings>(None),
1764            &UserSettings {
1765                name: "John Doe".to_string(),
1766                age: 30,
1767                staff: false,
1768            }
1769        );
1770
1771        store.register_setting::<MultiKeySettings>(cx);
1772        assert_eq!(
1773            store.get::<MultiKeySettings>(None),
1774            &MultiKeySettings {
1775                key1: "x".into(),
1776                key2: String::new(),
1777            }
1778        );
1779    }
1780
1781    fn check_settings_update<T: Settings>(
1782        store: &mut SettingsStore,
1783        old_json: String,
1784        update: fn(&mut T::FileContent),
1785        expected_new_json: String,
1786        cx: &mut App,
1787    ) {
1788        store.set_user_settings(&old_json, cx).ok();
1789        let edits = store.edits_for_update::<T>(&old_json, update);
1790        let mut new_json = old_json;
1791        for (range, replacement) in edits.into_iter() {
1792            new_json.replace_range(range, &replacement);
1793        }
1794        pretty_assertions::assert_eq!(new_json, expected_new_json);
1795    }
1796
1797    #[gpui::test]
1798    fn test_setting_store_update(cx: &mut App) {
1799        let mut store = SettingsStore::new(cx);
1800        store.register_setting::<MultiKeySettings>(cx);
1801        store.register_setting::<UserSettings>(cx);
1802        store.register_setting::<LanguageSettings>(cx);
1803
1804        // entries added and updated
1805        check_settings_update::<LanguageSettings>(
1806            &mut store,
1807            r#"{
1808                "languages": {
1809                    "JSON": {
1810                        "language_setting_1": true
1811                    }
1812                }
1813            }"#
1814            .unindent(),
1815            |settings| {
1816                settings
1817                    .languages
1818                    .get_mut("JSON")
1819                    .unwrap()
1820                    .language_setting_1 = Some(false);
1821                settings.languages.insert(
1822                    "Rust".into(),
1823                    LanguageSettingEntry {
1824                        language_setting_2: Some(true),
1825                        ..Default::default()
1826                    },
1827                );
1828            },
1829            r#"{
1830                "languages": {
1831                    "Rust": {
1832                        "language_setting_2": true
1833                    },
1834                    "JSON": {
1835                        "language_setting_1": false
1836                    }
1837                }
1838            }"#
1839            .unindent(),
1840            cx,
1841        );
1842
1843        // entries removed
1844        check_settings_update::<LanguageSettings>(
1845            &mut store,
1846            r#"{
1847                "languages": {
1848                    "Rust": {
1849                        "language_setting_2": true
1850                    },
1851                    "JSON": {
1852                        "language_setting_1": false
1853                    }
1854                }
1855            }"#
1856            .unindent(),
1857            |settings| {
1858                settings.languages.remove("JSON").unwrap();
1859            },
1860            r#"{
1861                "languages": {
1862                    "Rust": {
1863                        "language_setting_2": true
1864                    }
1865                }
1866            }"#
1867            .unindent(),
1868            cx,
1869        );
1870
1871        check_settings_update::<LanguageSettings>(
1872            &mut store,
1873            r#"{
1874                "languages": {
1875                    "Rust": {
1876                        "language_setting_2": true
1877                    },
1878                    "JSON": {
1879                        "language_setting_1": false
1880                    }
1881                }
1882            }"#
1883            .unindent(),
1884            |settings| {
1885                settings.languages.remove("Rust").unwrap();
1886            },
1887            r#"{
1888                "languages": {
1889                    "JSON": {
1890                        "language_setting_1": false
1891                    }
1892                }
1893            }"#
1894            .unindent(),
1895            cx,
1896        );
1897
1898        // weird formatting
1899        check_settings_update::<UserSettings>(
1900            &mut store,
1901            r#"{
1902                "user":   { "age": 36, "name": "Max", "staff": true }
1903                }"#
1904            .unindent(),
1905            |settings| settings.age = Some(37),
1906            r#"{
1907                "user":   { "age": 37, "name": "Max", "staff": true }
1908                }"#
1909            .unindent(),
1910            cx,
1911        );
1912
1913        // single-line formatting, other keys
1914        check_settings_update::<MultiKeySettings>(
1915            &mut store,
1916            r#"{ "one": 1, "two": 2 }"#.unindent(),
1917            |settings| settings.key1 = Some("x".into()),
1918            r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(),
1919            cx,
1920        );
1921
1922        // empty object
1923        check_settings_update::<UserSettings>(
1924            &mut store,
1925            r#"{
1926                "user": {}
1927            }"#
1928            .unindent(),
1929            |settings| settings.age = Some(37),
1930            r#"{
1931                "user": {
1932                    "age": 37
1933                }
1934            }"#
1935            .unindent(),
1936            cx,
1937        );
1938
1939        // no content
1940        check_settings_update::<UserSettings>(
1941            &mut store,
1942            r#""#.unindent(),
1943            |settings| settings.age = Some(37),
1944            r#"{
1945                "user": {
1946                    "age": 37
1947                }
1948            }
1949            "#
1950            .unindent(),
1951            cx,
1952        );
1953
1954        check_settings_update::<UserSettings>(
1955            &mut store,
1956            r#"{
1957            }
1958            "#
1959            .unindent(),
1960            |settings| settings.age = Some(37),
1961            r#"{
1962                "user": {
1963                    "age": 37
1964                }
1965            }
1966            "#
1967            .unindent(),
1968            cx,
1969        );
1970    }
1971
1972    #[gpui::test]
1973    fn test_vscode_import(cx: &mut App) {
1974        let mut store = SettingsStore::new(cx);
1975        store.register_setting::<UserSettings>(cx);
1976        store.register_setting::<JournalSettings>(cx);
1977        store.register_setting::<LanguageSettings>(cx);
1978        store.register_setting::<MultiKeySettings>(cx);
1979
1980        // create settings that werent present
1981        check_vscode_import(
1982            &mut store,
1983            r#"{
1984            }
1985            "#
1986            .unindent(),
1987            r#" { "user.age": 37 } "#.to_owned(),
1988            r#"{
1989                "user": {
1990                    "age": 37
1991                }
1992            }
1993            "#
1994            .unindent(),
1995            cx,
1996        );
1997
1998        // persist settings that were present
1999        check_vscode_import(
2000            &mut store,
2001            r#"{
2002                "user": {
2003                    "staff": true,
2004                    "age": 37
2005                }
2006            }
2007            "#
2008            .unindent(),
2009            r#"{ "user.age": 42 }"#.to_owned(),
2010            r#"{
2011                "user": {
2012                    "staff": true,
2013                    "age": 42
2014                }
2015            }
2016            "#
2017            .unindent(),
2018            cx,
2019        );
2020
2021        // don't clobber settings that aren't present in vscode
2022        check_vscode_import(
2023            &mut store,
2024            r#"{
2025                "user": {
2026                    "staff": true,
2027                    "age": 37
2028                }
2029            }
2030            "#
2031            .unindent(),
2032            r#"{}"#.to_owned(),
2033            r#"{
2034                "user": {
2035                    "staff": true,
2036                    "age": 37
2037                }
2038            }
2039            "#
2040            .unindent(),
2041            cx,
2042        );
2043
2044        // custom enum
2045        check_vscode_import(
2046            &mut store,
2047            r#"{
2048                "journal": {
2049                "hour_format": "hour12"
2050                }
2051            }
2052            "#
2053            .unindent(),
2054            r#"{ "time_format": "24" }"#.to_owned(),
2055            r#"{
2056                "journal": {
2057                "hour_format": "hour24"
2058                }
2059            }
2060            "#
2061            .unindent(),
2062            cx,
2063        );
2064
2065        // Multiple keys for one setting
2066        check_vscode_import(
2067            &mut store,
2068            r#"{
2069                "key1": "value"
2070            }
2071            "#
2072            .unindent(),
2073            r#"{
2074                "key_1_first": "hello",
2075                "key_1_second": "world"
2076            }"#
2077            .to_owned(),
2078            r#"{
2079                "key1": "hello world"
2080            }
2081            "#
2082            .unindent(),
2083            cx,
2084        );
2085
2086        // Merging lists together entries added and updated
2087        check_vscode_import(
2088            &mut store,
2089            r#"{
2090                "languages": {
2091                    "JSON": {
2092                        "language_setting_1": true
2093                    },
2094                    "Rust": {
2095                        "language_setting_2": true
2096                    }
2097                }
2098            }"#
2099            .unindent(),
2100            r#"{
2101                "vscode_languages": [
2102                    {
2103                        "name": "JavaScript",
2104                        "language_setting_1": true
2105                    },
2106                    {
2107                        "name": "Rust",
2108                        "language_setting_2": false
2109                    }
2110                ]
2111            }"#
2112            .to_owned(),
2113            r#"{
2114                "languages": {
2115                    "JavaScript": {
2116                        "language_setting_1": true
2117                    },
2118                    "JSON": {
2119                        "language_setting_1": true
2120                    },
2121                    "Rust": {
2122                        "language_setting_2": false
2123                    }
2124                }
2125            }"#
2126            .unindent(),
2127            cx,
2128        );
2129    }
2130
2131    fn check_vscode_import(
2132        store: &mut SettingsStore,
2133        old: String,
2134        vscode: String,
2135        expected: String,
2136        cx: &mut App,
2137    ) {
2138        store.set_user_settings(&old, cx).ok();
2139        let new = store.get_vscode_edits(
2140            old,
2141            &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
2142        );
2143        pretty_assertions::assert_eq!(new, expected);
2144    }
2145
2146    #[derive(Debug, PartialEq, Deserialize, SettingsUi)]
2147    struct UserSettings {
2148        name: String,
2149        age: u32,
2150        staff: bool,
2151    }
2152
2153    #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
2154    #[settings_key(key = "user")]
2155    struct UserSettingsContent {
2156        name: Option<String>,
2157        age: Option<u32>,
2158        staff: Option<bool>,
2159    }
2160
2161    impl Settings for UserSettings {
2162        type FileContent = UserSettingsContent;
2163
2164        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
2165            sources.json_merge()
2166        }
2167
2168        fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
2169            vscode.u32_setting("user.age", &mut current.age);
2170        }
2171    }
2172
2173    #[derive(Debug, Deserialize, PartialEq)]
2174    struct TurboSetting(bool);
2175
2176    #[derive(
2177        Copy,
2178        Clone,
2179        PartialEq,
2180        Eq,
2181        Debug,
2182        Default,
2183        serde::Serialize,
2184        serde::Deserialize,
2185        SettingsUi,
2186        SettingsKey,
2187        JsonSchema,
2188    )]
2189    #[serde(default)]
2190    #[settings_key(None)]
2191    pub struct TurboSettingContent {
2192        turbo: Option<bool>,
2193    }
2194
2195    impl Settings for TurboSetting {
2196        type FileContent = TurboSettingContent;
2197
2198        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
2199            Ok(Self(
2200                sources
2201                    .user
2202                    .or(sources.server)
2203                    .unwrap_or(sources.default)
2204                    .turbo
2205                    .unwrap_or_default(),
2206            ))
2207        }
2208
2209        fn import_from_vscode(_vscode: &VsCodeSettings, _current: &mut Self::FileContent) {}
2210    }
2211
2212    #[derive(Clone, Debug, PartialEq, Deserialize)]
2213    struct MultiKeySettings {
2214        #[serde(default)]
2215        key1: String,
2216        #[serde(default)]
2217        key2: String,
2218    }
2219
2220    #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
2221    #[settings_key(None)]
2222    struct MultiKeySettingsJson {
2223        key1: Option<String>,
2224        key2: Option<String>,
2225    }
2226
2227    impl Settings for MultiKeySettings {
2228        type FileContent = MultiKeySettingsJson;
2229
2230        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
2231            sources.json_merge()
2232        }
2233
2234        fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
2235            let first_value = vscode.read_string("key_1_first");
2236            let second_value = vscode.read_string("key_1_second");
2237
2238            if let Some((first, second)) = first_value.zip(second_value) {
2239                current.key1 = Some(format!("{} {}", first, second));
2240            }
2241        }
2242    }
2243
2244    #[derive(Debug, Deserialize)]
2245    struct JournalSettings {
2246        #[expect(unused)]
2247        pub path: String,
2248        #[expect(unused)]
2249        pub hour_format: HourFormat,
2250    }
2251
2252    #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2253    #[serde(rename_all = "snake_case")]
2254    enum HourFormat {
2255        Hour12,
2256        Hour24,
2257    }
2258
2259    #[derive(
2260        Clone, Default, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey,
2261    )]
2262    #[settings_key(key = "journal")]
2263    struct JournalSettingsJson {
2264        pub path: Option<String>,
2265        pub hour_format: Option<HourFormat>,
2266    }
2267
2268    impl Settings for JournalSettings {
2269        type FileContent = JournalSettingsJson;
2270
2271        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
2272            sources.json_merge()
2273        }
2274
2275        fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
2276            vscode.enum_setting("time_format", &mut current.hour_format, |s| match s {
2277                "12" => Some(HourFormat::Hour12),
2278                "24" => Some(HourFormat::Hour24),
2279                _ => None,
2280            });
2281        }
2282    }
2283
2284    #[gpui::test]
2285    fn test_global_settings(cx: &mut App) {
2286        let mut store = SettingsStore::new(cx);
2287        store.register_setting::<UserSettings>(cx);
2288        store
2289            .set_default_settings(
2290                r#"{
2291                    "user": {
2292                        "name": "John Doe",
2293                        "age": 30,
2294                        "staff": false
2295                    }
2296                }"#,
2297                cx,
2298            )
2299            .unwrap();
2300
2301        // Set global settings - these should override defaults but not user settings
2302        store
2303            .set_global_settings(
2304                r#"{
2305                    "user": {
2306                        "name": "Global User",
2307                        "age": 35,
2308                        "staff": true
2309                    }
2310                }"#,
2311                cx,
2312            )
2313            .unwrap();
2314
2315        // Before user settings, global settings should apply
2316        assert_eq!(
2317            store.get::<UserSettings>(None),
2318            &UserSettings {
2319                name: "Global User".to_string(),
2320                age: 35,
2321                staff: true,
2322            }
2323        );
2324
2325        // Set user settings - these should override both defaults and global
2326        store
2327            .set_user_settings(
2328                r#"{
2329                    "user": {
2330                        "age": 40
2331                    }
2332                }"#,
2333                cx,
2334            )
2335            .unwrap();
2336
2337        // User settings should override global settings
2338        assert_eq!(
2339            store.get::<UserSettings>(None),
2340            &UserSettings {
2341                name: "Global User".to_string(), // Name from global settings
2342                age: 40,                         // Age from user settings
2343                staff: true,                     // Staff from global settings
2344            }
2345        );
2346    }
2347
2348    #[derive(
2349        Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey,
2350    )]
2351    #[settings_key(None)]
2352    struct LanguageSettings {
2353        #[serde(default)]
2354        languages: HashMap<String, LanguageSettingEntry>,
2355    }
2356
2357    #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
2358    struct LanguageSettingEntry {
2359        language_setting_1: Option<bool>,
2360        language_setting_2: Option<bool>,
2361    }
2362
2363    impl Settings for LanguageSettings {
2364        type FileContent = Self;
2365
2366        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
2367            sources.json_merge()
2368        }
2369
2370        fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
2371            current.languages.extend(
2372                vscode
2373                    .read_value("vscode_languages")
2374                    .and_then(|value| value.as_array())
2375                    .map(|languages| {
2376                        languages
2377                            .iter()
2378                            .filter_map(|value| value.as_object())
2379                            .filter_map(|item| {
2380                                let mut rest = item.clone();
2381                                let name = rest.remove("name")?.as_str()?.to_string();
2382                                let entry = serde_json::from_value::<LanguageSettingEntry>(
2383                                    serde_json::Value::Object(rest),
2384                                )
2385                                .ok()?;
2386
2387                                Some((name, entry))
2388                            })
2389                    })
2390                    .into_iter()
2391                    .flatten(),
2392            );
2393        }
2394    }
2395}