settings_store.rs

   1use anyhow::{Context as _, Result, anyhow};
   2use collections::{BTreeMap, HashMap, btree_map, hash_map};
   3use ec4rs::{ConfigParser, PropertiesSource, Section};
   4use fs::Fs;
   5use futures::{FutureExt, StreamExt, channel::mpsc, future::LocalBoxFuture};
   6use gpui::{App, AsyncApp, BorrowAppContext, Global, Task, UpdateGlobal};
   7
   8use paths::{
   9    EDITORCONFIG_NAME, debug_task_file_name, local_settings_file_relative_path, task_file_name,
  10};
  11use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema};
  12use serde::{Deserialize, Serialize, de::DeserializeOwned};
  13use smallvec::SmallVec;
  14use std::{
  15    any::{Any, TypeId, type_name},
  16    fmt::Debug,
  17    ops::Range,
  18    path::{Path, PathBuf},
  19    str::{self, FromStr},
  20    sync::{Arc, LazyLock},
  21};
  22use streaming_iterator::StreamingIterator;
  23use tree_sitter::Query;
  24use util::RangeExt;
  25
  26use util::{ResultExt as _, merge_non_null_json_value_into};
  27
  28pub type EditorconfigProperties = ec4rs::Properties;
  29
  30use crate::{SettingsJsonSchemaParams, WorktreeId};
  31
  32/// A value that can be defined as a user setting.
  33///
  34/// Settings can be loaded from a combination of multiple JSON files.
  35pub trait Settings: 'static + Send + Sync {
  36    /// The name of a key within the JSON file from which this setting should
  37    /// be deserialized. If this is `None`, then the setting will be deserialized
  38    /// from the root object.
  39    const KEY: Option<&'static str>;
  40
  41    /// The name of the keys in the [`FileContent`](Self::FileContent) that should
  42    /// always be written to a settings file, even if their value matches the default
  43    /// value.
  44    ///
  45    /// This is useful for tagged [`FileContent`](Self::FileContent)s where the tag
  46    /// is a "version" field that should always be persisted, even if the current
  47    /// user settings match the current version of the settings.
  48    const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
  49
  50    /// The type that is stored in an individual JSON file.
  51    type FileContent: Clone + Default + Serialize + DeserializeOwned + JsonSchema;
  52
  53    /// The logic for combining together values from one or more JSON files into the
  54    /// final value for this setting.
  55    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self>
  56    where
  57        Self: Sized;
  58
  59    fn json_schema(
  60        generator: &mut SchemaGenerator,
  61        _: &SettingsJsonSchemaParams,
  62        _: &App,
  63    ) -> RootSchema {
  64        generator.root_schema_for::<Self::FileContent>()
  65    }
  66
  67    fn missing_default() -> anyhow::Error {
  68        anyhow::anyhow!("missing default")
  69    }
  70
  71    #[track_caller]
  72    fn register(cx: &mut App)
  73    where
  74        Self: Sized,
  75    {
  76        SettingsStore::update_global(cx, |store, cx| {
  77            store.register_setting::<Self>(cx);
  78        });
  79    }
  80
  81    #[track_caller]
  82    fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self
  83    where
  84        Self: Sized,
  85    {
  86        cx.global::<SettingsStore>().get(path)
  87    }
  88
  89    #[track_caller]
  90    fn get_global(cx: &App) -> &Self
  91    where
  92        Self: Sized,
  93    {
  94        cx.global::<SettingsStore>().get(None)
  95    }
  96
  97    #[track_caller]
  98    fn try_read_global<R>(cx: &AsyncApp, f: impl FnOnce(&Self) -> R) -> Option<R>
  99    where
 100        Self: Sized,
 101    {
 102        cx.try_read_global(|s: &SettingsStore, _| f(s.get(None)))
 103    }
 104
 105    #[track_caller]
 106    fn override_global(settings: Self, cx: &mut App)
 107    where
 108        Self: Sized,
 109    {
 110        cx.global_mut::<SettingsStore>().override_global(settings)
 111    }
 112}
 113
 114#[derive(Clone, Copy, Debug)]
 115pub struct SettingsSources<'a, T> {
 116    /// The default Zed settings.
 117    pub default: &'a T,
 118    /// Settings provided by extensions.
 119    pub extensions: Option<&'a T>,
 120    /// The user settings.
 121    pub user: Option<&'a T>,
 122    /// The user settings for the current release channel.
 123    pub release_channel: Option<&'a T>,
 124    /// The server's settings.
 125    pub server: Option<&'a T>,
 126    /// The project settings, ordered from least specific to most specific.
 127    pub project: &'a [&'a T],
 128}
 129
 130impl<'a, T: Serialize> SettingsSources<'a, T> {
 131    /// Returns an iterator over the default settings as well as all settings customizations.
 132    pub fn defaults_and_customizations(&self) -> impl Iterator<Item = &T> {
 133        [self.default].into_iter().chain(self.customizations())
 134    }
 135
 136    /// Returns an iterator over all of the settings customizations.
 137    pub fn customizations(&self) -> impl Iterator<Item = &T> {
 138        self.extensions
 139            .into_iter()
 140            .chain(self.user)
 141            .chain(self.release_channel)
 142            .chain(self.server)
 143            .chain(self.project.iter().copied())
 144    }
 145
 146    /// Returns the settings after performing a JSON merge of the provided customizations.
 147    ///
 148    /// Customizations later in the iterator win out over the earlier ones.
 149    pub fn json_merge_with<O: DeserializeOwned>(
 150        customizations: impl Iterator<Item = &'a T>,
 151    ) -> Result<O> {
 152        let mut merged = serde_json::Value::Null;
 153        for value in customizations {
 154            merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
 155        }
 156        Ok(serde_json::from_value(merged)?)
 157    }
 158
 159    /// Returns the settings after performing a JSON merge of the customizations into the
 160    /// default settings.
 161    ///
 162    /// More-specific customizations win out over the less-specific ones.
 163    pub fn json_merge<O: DeserializeOwned>(&'a self) -> Result<O> {
 164        Self::json_merge_with(self.defaults_and_customizations())
 165    }
 166}
 167
 168#[derive(Clone, Copy, Debug)]
 169pub struct SettingsLocation<'a> {
 170    pub worktree_id: WorktreeId,
 171    pub path: &'a Path,
 172}
 173
 174/// A set of strongly-typed setting values defined via multiple config files.
 175pub struct SettingsStore {
 176    setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
 177    raw_default_settings: serde_json::Value,
 178    raw_user_settings: serde_json::Value,
 179    raw_server_settings: Option<serde_json::Value>,
 180    raw_extension_settings: serde_json::Value,
 181    raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), serde_json::Value>,
 182    raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>,
 183    tab_size_callback: Option<(
 184        TypeId,
 185        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,
 186    )>,
 187    _setting_file_updates: Task<()>,
 188    setting_file_updates_tx:
 189        mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,
 190}
 191
 192#[derive(Clone)]
 193pub struct Editorconfig {
 194    pub is_root: bool,
 195    pub sections: SmallVec<[Section; 5]>,
 196}
 197
 198impl FromStr for Editorconfig {
 199    type Err = anyhow::Error;
 200
 201    fn from_str(contents: &str) -> Result<Self, Self::Err> {
 202        let parser = ConfigParser::new_buffered(contents.as_bytes())
 203            .context("creating editorconfig parser")?;
 204        let is_root = parser.is_root;
 205        let sections = parser
 206            .collect::<Result<SmallVec<_>, _>>()
 207            .context("parsing editorconfig sections")?;
 208        Ok(Self { is_root, sections })
 209    }
 210}
 211
 212#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 213pub enum LocalSettingsKind {
 214    Settings,
 215    Tasks(TaskKind),
 216    Editorconfig,
 217}
 218
 219#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 220pub enum TaskKind {
 221    Debug,
 222    Script,
 223}
 224
 225impl Global for SettingsStore {}
 226
 227#[derive(Debug)]
 228struct SettingValue<T> {
 229    global_value: Option<T>,
 230    local_values: Vec<(WorktreeId, Arc<Path>, T)>,
 231}
 232
 233trait AnySettingValue: 'static + Send + Sync {
 234    fn key(&self) -> Option<&'static str>;
 235    fn setting_type_name(&self) -> &'static str;
 236    fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting>;
 237    fn load_setting(
 238        &self,
 239        sources: SettingsSources<DeserializedSetting>,
 240        cx: &mut App,
 241    ) -> Result<Box<dyn Any>>;
 242    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
 243    fn set_global_value(&mut self, value: Box<dyn Any>);
 244    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>);
 245    fn json_schema(
 246        &self,
 247        generator: &mut SchemaGenerator,
 248        _: &SettingsJsonSchemaParams,
 249        cx: &App,
 250    ) -> RootSchema;
 251}
 252
 253struct DeserializedSetting(Box<dyn Any>);
 254
 255impl TaskKind {
 256    /// Returns a file path of a task configuration file of this kind within the given directory.
 257    pub fn config_in_dir(&self, dir: &Path) -> PathBuf {
 258        dir.join(match self {
 259            Self::Debug => debug_task_file_name(),
 260            Self::Script => task_file_name(),
 261        })
 262    }
 263}
 264
 265impl SettingsStore {
 266    pub fn new(cx: &App) -> Self {
 267        let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
 268        Self {
 269            setting_values: Default::default(),
 270            raw_default_settings: serde_json::json!({}),
 271            raw_user_settings: serde_json::json!({}),
 272            raw_server_settings: None,
 273            raw_extension_settings: serde_json::json!({}),
 274            raw_local_settings: Default::default(),
 275            raw_editorconfig_settings: BTreeMap::default(),
 276            tab_size_callback: Default::default(),
 277            setting_file_updates_tx,
 278            _setting_file_updates: cx.spawn(async move |cx| {
 279                while let Some(setting_file_update) = setting_file_updates_rx.next().await {
 280                    (setting_file_update)(cx.clone()).await.log_err();
 281                }
 282            }),
 283        }
 284    }
 285
 286    pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R
 287    where
 288        C: BorrowAppContext,
 289    {
 290        cx.update_global(f)
 291    }
 292
 293    /// Add a new type of setting to the store.
 294    pub fn register_setting<T: Settings>(&mut self, cx: &mut App) {
 295        let setting_type_id = TypeId::of::<T>();
 296        let entry = self.setting_values.entry(setting_type_id);
 297
 298        if matches!(entry, hash_map::Entry::Occupied(_)) {
 299            return;
 300        }
 301
 302        let setting_value = entry.or_insert(Box::new(SettingValue::<T> {
 303            global_value: None,
 304            local_values: Vec::new(),
 305        }));
 306
 307        if let Some(default_settings) = setting_value
 308            .deserialize_setting(&self.raw_default_settings)
 309            .log_err()
 310        {
 311            let user_value = setting_value
 312                .deserialize_setting(&self.raw_user_settings)
 313                .log_err();
 314
 315            let mut release_channel_value = None;
 316            if let Some(release_settings) = &self
 317                .raw_user_settings
 318                .get(release_channel::RELEASE_CHANNEL.dev_name())
 319            {
 320                release_channel_value = setting_value
 321                    .deserialize_setting(release_settings)
 322                    .log_err();
 323            }
 324
 325            let server_value = self
 326                .raw_server_settings
 327                .as_ref()
 328                .and_then(|server_setting| {
 329                    setting_value.deserialize_setting(server_setting).log_err()
 330                });
 331
 332            let extension_value = setting_value
 333                .deserialize_setting(&self.raw_extension_settings)
 334                .log_err();
 335
 336            if let Some(setting) = setting_value
 337                .load_setting(
 338                    SettingsSources {
 339                        default: &default_settings,
 340                        extensions: extension_value.as_ref(),
 341                        user: user_value.as_ref(),
 342                        release_channel: release_channel_value.as_ref(),
 343                        server: server_value.as_ref(),
 344                        project: &[],
 345                    },
 346                    cx,
 347                )
 348                .context("A default setting must be added to the `default.json` file")
 349                .log_err()
 350            {
 351                setting_value.set_global_value(setting);
 352            }
 353        }
 354    }
 355
 356    /// Get the value of a setting.
 357    ///
 358    /// Panics if the given setting type has not been registered, or if there is no
 359    /// value for this setting.
 360    pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T {
 361        self.setting_values
 362            .get(&TypeId::of::<T>())
 363            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 364            .value_for_path(path)
 365            .downcast_ref::<T>()
 366            .expect("no default value for setting type")
 367    }
 368
 369    /// Override the global value for a setting.
 370    ///
 371    /// The given value will be overwritten if the user settings file changes.
 372    pub fn override_global<T: Settings>(&mut self, value: T) {
 373        self.setting_values
 374            .get_mut(&TypeId::of::<T>())
 375            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 376            .set_global_value(Box::new(value))
 377    }
 378
 379    /// Get the user's settings as a raw JSON value.
 380    ///
 381    /// For user-facing functionality use the typed setting interface.
 382    /// (e.g. ProjectSettings::get_global(cx))
 383    pub fn raw_user_settings(&self) -> &serde_json::Value {
 384        &self.raw_user_settings
 385    }
 386
 387    #[cfg(any(test, feature = "test-support"))]
 388    pub fn test(cx: &mut App) -> Self {
 389        let mut this = Self::new(cx);
 390        this.set_default_settings(&crate::test_settings(), cx)
 391            .unwrap();
 392        this.set_user_settings("{}", cx).unwrap();
 393        this
 394    }
 395
 396    /// Updates the value of a setting in the user's global configuration.
 397    ///
 398    /// This is only for tests. Normally, settings are only loaded from
 399    /// JSON files.
 400    #[cfg(any(test, feature = "test-support"))]
 401    pub fn update_user_settings<T: Settings>(
 402        &mut self,
 403        cx: &mut App,
 404        update: impl FnOnce(&mut T::FileContent),
 405    ) {
 406        let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();
 407        let new_text = self.new_text_for_update::<T>(old_text, update);
 408        self.set_user_settings(&new_text, cx).unwrap();
 409    }
 410
 411    pub async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {
 412        match fs.load(paths::settings_file()).await {
 413            result @ Ok(_) => result,
 414            Err(err) => {
 415                if let Some(e) = err.downcast_ref::<std::io::Error>() {
 416                    if e.kind() == std::io::ErrorKind::NotFound {
 417                        return Ok(crate::initial_user_settings_content().to_string());
 418                    }
 419                }
 420                Err(err)
 421            }
 422        }
 423    }
 424
 425    pub fn update_settings_file<T: Settings>(
 426        &self,
 427        fs: Arc<dyn Fs>,
 428        update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
 429    ) {
 430        self.setting_file_updates_tx
 431            .unbounded_send(Box::new(move |cx: AsyncApp| {
 432                async move {
 433                    let old_text = Self::load_settings(&fs).await?;
 434                    let new_text = cx.read_global(|store: &SettingsStore, cx| {
 435                        store.new_text_for_update::<T>(old_text, |content| update(content, cx))
 436                    })?;
 437                    let settings_path = paths::settings_file().as_path();
 438                    if fs.is_file(settings_path).await {
 439                        let resolved_path =
 440                            fs.canonicalize(settings_path).await.with_context(|| {
 441                                format!("Failed to canonicalize settings path {:?}", settings_path)
 442                            })?;
 443
 444                        fs.atomic_write(resolved_path.clone(), new_text)
 445                            .await
 446                            .with_context(|| {
 447                                format!("Failed to write settings to file {:?}", resolved_path)
 448                            })?;
 449                    } else {
 450                        fs.atomic_write(settings_path.to_path_buf(), new_text)
 451                            .await
 452                            .with_context(|| {
 453                                format!("Failed to write settings to file {:?}", settings_path)
 454                            })?;
 455                    }
 456
 457                    anyhow::Ok(())
 458                }
 459                .boxed_local()
 460            }))
 461            .ok();
 462    }
 463
 464    /// Updates the value of a setting in a JSON file, returning the new text
 465    /// for that JSON file.
 466    pub fn new_text_for_update<T: Settings>(
 467        &self,
 468        old_text: String,
 469        update: impl FnOnce(&mut T::FileContent),
 470    ) -> String {
 471        let edits = self.edits_for_update::<T>(&old_text, update);
 472        let mut new_text = old_text;
 473        for (range, replacement) in edits.into_iter() {
 474            new_text.replace_range(range, &replacement);
 475        }
 476        new_text
 477    }
 478
 479    /// Updates the value of a setting in a JSON file, returning a list
 480    /// of edits to apply to the JSON file.
 481    pub fn edits_for_update<T: Settings>(
 482        &self,
 483        text: &str,
 484        update: impl FnOnce(&mut T::FileContent),
 485    ) -> Vec<(Range<usize>, String)> {
 486        let setting_type_id = TypeId::of::<T>();
 487
 488        let preserved_keys = T::PRESERVED_KEYS.unwrap_or_default();
 489
 490        let setting = self
 491            .setting_values
 492            .get(&setting_type_id)
 493            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()));
 494        let raw_settings = parse_json_with_comments::<serde_json::Value>(text).unwrap_or_default();
 495        let old_content = match setting.deserialize_setting(&raw_settings) {
 496            Ok(content) => content.0.downcast::<T::FileContent>().unwrap(),
 497            Err(_) => Box::<<T as Settings>::FileContent>::default(),
 498        };
 499        let mut new_content = old_content.clone();
 500        update(&mut new_content);
 501
 502        let old_value = serde_json::to_value(&old_content).unwrap();
 503        let new_value = serde_json::to_value(new_content).unwrap();
 504
 505        let mut key_path = Vec::new();
 506        if let Some(key) = T::KEY {
 507            key_path.push(key);
 508        }
 509
 510        let mut edits = Vec::new();
 511        let tab_size = self.json_tab_size();
 512        let mut text = text.to_string();
 513        update_value_in_json_text(
 514            &mut text,
 515            &mut key_path,
 516            tab_size,
 517            &old_value,
 518            &new_value,
 519            preserved_keys,
 520            &mut edits,
 521        );
 522        edits
 523    }
 524
 525    /// Configure the tab sized when updating JSON files.
 526    pub fn set_json_tab_size_callback<T: Settings>(
 527        &mut self,
 528        get_tab_size: fn(&T) -> Option<usize>,
 529    ) {
 530        self.tab_size_callback = Some((
 531            TypeId::of::<T>(),
 532            Box::new(move |value| get_tab_size(value.downcast_ref::<T>().unwrap())),
 533        ));
 534    }
 535
 536    fn json_tab_size(&self) -> usize {
 537        const DEFAULT_JSON_TAB_SIZE: usize = 2;
 538
 539        if let Some((setting_type_id, callback)) = &self.tab_size_callback {
 540            let setting_value = self.setting_values.get(setting_type_id).unwrap();
 541            let value = setting_value.value_for_path(None);
 542            if let Some(value) = callback(value) {
 543                return value;
 544            }
 545        }
 546
 547        DEFAULT_JSON_TAB_SIZE
 548    }
 549
 550    /// Sets the default settings via a JSON string.
 551    ///
 552    /// The string should contain a JSON object with a default value for every setting.
 553    pub fn set_default_settings(
 554        &mut self,
 555        default_settings_content: &str,
 556        cx: &mut App,
 557    ) -> Result<()> {
 558        let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?;
 559        if settings.is_object() {
 560            self.raw_default_settings = settings;
 561            self.recompute_values(None, cx)?;
 562            Ok(())
 563        } else {
 564            Err(anyhow!("settings must be an object"))
 565        }
 566    }
 567
 568    /// Sets the user settings via a JSON string.
 569    pub fn set_user_settings(
 570        &mut self,
 571        user_settings_content: &str,
 572        cx: &mut App,
 573    ) -> Result<serde_json::Value> {
 574        let settings: serde_json::Value = if user_settings_content.is_empty() {
 575            parse_json_with_comments("{}")?
 576        } else {
 577            parse_json_with_comments(user_settings_content)?
 578        };
 579
 580        anyhow::ensure!(settings.is_object(), "settings must be an object");
 581        self.raw_user_settings = settings.clone();
 582        self.recompute_values(None, cx)?;
 583        Ok(settings)
 584    }
 585
 586    pub fn set_server_settings(
 587        &mut self,
 588        server_settings_content: &str,
 589        cx: &mut App,
 590    ) -> Result<()> {
 591        let settings: Option<serde_json::Value> = if server_settings_content.is_empty() {
 592            None
 593        } else {
 594            parse_json_with_comments(server_settings_content)?
 595        };
 596
 597        anyhow::ensure!(
 598            settings
 599                .as_ref()
 600                .map(|value| value.is_object())
 601                .unwrap_or(true),
 602            "settings must be an object"
 603        );
 604        self.raw_server_settings = settings;
 605        self.recompute_values(None, cx)?;
 606        Ok(())
 607    }
 608
 609    /// Add or remove a set of local settings via a JSON string.
 610    pub fn set_local_settings(
 611        &mut self,
 612        root_id: WorktreeId,
 613        directory_path: Arc<Path>,
 614        kind: LocalSettingsKind,
 615        settings_content: Option<&str>,
 616        cx: &mut App,
 617    ) -> std::result::Result<(), InvalidSettingsError> {
 618        let mut zed_settings_changed = false;
 619        match (
 620            kind,
 621            settings_content
 622                .map(|content| content.trim())
 623                .filter(|content| !content.is_empty()),
 624        ) {
 625            (LocalSettingsKind::Tasks(task_kind), _) => {
 626                return Err(InvalidSettingsError::Tasks {
 627                    message: "Attempted to submit tasks into the settings store".to_string(),
 628                    path: task_kind.config_in_dir(&directory_path),
 629                });
 630            }
 631            (LocalSettingsKind::Settings, None) => {
 632                zed_settings_changed = self
 633                    .raw_local_settings
 634                    .remove(&(root_id, directory_path.clone()))
 635                    .is_some()
 636            }
 637            (LocalSettingsKind::Editorconfig, None) => {
 638                self.raw_editorconfig_settings
 639                    .remove(&(root_id, directory_path.clone()));
 640            }
 641            (LocalSettingsKind::Settings, Some(settings_contents)) => {
 642                let new_settings = parse_json_with_comments::<serde_json::Value>(settings_contents)
 643                    .map_err(|e| InvalidSettingsError::LocalSettings {
 644                        path: directory_path.join(local_settings_file_relative_path()),
 645                        message: e.to_string(),
 646                    })?;
 647                match self
 648                    .raw_local_settings
 649                    .entry((root_id, directory_path.clone()))
 650                {
 651                    btree_map::Entry::Vacant(v) => {
 652                        v.insert(new_settings);
 653                        zed_settings_changed = true;
 654                    }
 655                    btree_map::Entry::Occupied(mut o) => {
 656                        if o.get() != &new_settings {
 657                            o.insert(new_settings);
 658                            zed_settings_changed = true;
 659                        }
 660                    }
 661                }
 662            }
 663            (LocalSettingsKind::Editorconfig, Some(editorconfig_contents)) => {
 664                match self
 665                    .raw_editorconfig_settings
 666                    .entry((root_id, directory_path.clone()))
 667                {
 668                    btree_map::Entry::Vacant(v) => match editorconfig_contents.parse() {
 669                        Ok(new_contents) => {
 670                            v.insert((editorconfig_contents.to_owned(), Some(new_contents)));
 671                        }
 672                        Err(e) => {
 673                            v.insert((editorconfig_contents.to_owned(), None));
 674                            return Err(InvalidSettingsError::Editorconfig {
 675                                message: e.to_string(),
 676                                path: directory_path.join(EDITORCONFIG_NAME),
 677                            });
 678                        }
 679                    },
 680                    btree_map::Entry::Occupied(mut o) => {
 681                        if o.get().0 != editorconfig_contents {
 682                            match editorconfig_contents.parse() {
 683                                Ok(new_contents) => {
 684                                    o.insert((
 685                                        editorconfig_contents.to_owned(),
 686                                        Some(new_contents),
 687                                    ));
 688                                }
 689                                Err(e) => {
 690                                    o.insert((editorconfig_contents.to_owned(), None));
 691                                    return Err(InvalidSettingsError::Editorconfig {
 692                                        message: e.to_string(),
 693                                        path: directory_path.join(EDITORCONFIG_NAME),
 694                                    });
 695                                }
 696                            }
 697                        }
 698                    }
 699                }
 700            }
 701        };
 702
 703        if zed_settings_changed {
 704            self.recompute_values(Some((root_id, &directory_path)), cx)?;
 705        }
 706        Ok(())
 707    }
 708
 709    pub fn set_extension_settings<T: Serialize>(&mut self, content: T, cx: &mut App) -> Result<()> {
 710        let settings: serde_json::Value = serde_json::to_value(content)?;
 711        anyhow::ensure!(settings.is_object(), "settings must be an object");
 712        self.raw_extension_settings = settings;
 713        self.recompute_values(None, cx)?;
 714        Ok(())
 715    }
 716
 717    /// Add or remove a set of local settings via a JSON string.
 718    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
 719        self.raw_local_settings
 720            .retain(|(worktree_id, _), _| worktree_id != &root_id);
 721        self.recompute_values(Some((root_id, "".as_ref())), cx)?;
 722        Ok(())
 723    }
 724
 725    pub fn local_settings(
 726        &self,
 727        root_id: WorktreeId,
 728    ) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
 729        self.raw_local_settings
 730            .range(
 731                (root_id, Path::new("").into())
 732                    ..(
 733                        WorktreeId::from_usize(root_id.to_usize() + 1),
 734                        Path::new("").into(),
 735                    ),
 736            )
 737            .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))
 738    }
 739
 740    pub fn local_editorconfig_settings(
 741        &self,
 742        root_id: WorktreeId,
 743    ) -> impl '_ + Iterator<Item = (Arc<Path>, String, Option<Editorconfig>)> {
 744        self.raw_editorconfig_settings
 745            .range(
 746                (root_id, Path::new("").into())
 747                    ..(
 748                        WorktreeId::from_usize(root_id.to_usize() + 1),
 749                        Path::new("").into(),
 750                    ),
 751            )
 752            .map(|((_, path), (content, parsed_content))| {
 753                (path.clone(), content.clone(), parsed_content.clone())
 754            })
 755    }
 756
 757    pub fn json_schema(
 758        &self,
 759        schema_params: &SettingsJsonSchemaParams,
 760        cx: &App,
 761    ) -> serde_json::Value {
 762        use schemars::{
 763            r#gen::SchemaSettings,
 764            schema::{Schema, SchemaObject},
 765        };
 766
 767        let settings = SchemaSettings::draft07().with(|settings| {
 768            settings.option_add_null_type = true;
 769        });
 770        let mut generator = SchemaGenerator::new(settings);
 771        let mut combined_schema = RootSchema::default();
 772
 773        for setting_value in self.setting_values.values() {
 774            let setting_schema = setting_value.json_schema(&mut generator, schema_params, cx);
 775            combined_schema
 776                .definitions
 777                .extend(setting_schema.definitions);
 778
 779            let target_schema = if let Some(key) = setting_value.key() {
 780                let key_schema = combined_schema
 781                    .schema
 782                    .object()
 783                    .properties
 784                    .entry(key.to_string())
 785                    .or_insert_with(|| Schema::Object(SchemaObject::default()));
 786                if let Schema::Object(key_schema) = key_schema {
 787                    key_schema
 788                } else {
 789                    continue;
 790                }
 791            } else {
 792                &mut combined_schema.schema
 793            };
 794
 795            merge_schema(target_schema, setting_schema.schema);
 796        }
 797
 798        fn merge_schema(target: &mut SchemaObject, mut source: SchemaObject) {
 799            let source_subschemas = source.subschemas();
 800            let target_subschemas = target.subschemas();
 801            if let Some(all_of) = source_subschemas.all_of.take() {
 802                target_subschemas
 803                    .all_of
 804                    .get_or_insert(Vec::new())
 805                    .extend(all_of);
 806            }
 807            if let Some(any_of) = source_subschemas.any_of.take() {
 808                target_subschemas
 809                    .any_of
 810                    .get_or_insert(Vec::new())
 811                    .extend(any_of);
 812            }
 813            if let Some(one_of) = source_subschemas.one_of.take() {
 814                target_subschemas
 815                    .one_of
 816                    .get_or_insert(Vec::new())
 817                    .extend(one_of);
 818            }
 819
 820            if let Some(source) = source.object {
 821                let target_properties = &mut target.object().properties;
 822                for (key, value) in source.properties {
 823                    match target_properties.entry(key) {
 824                        btree_map::Entry::Vacant(e) => {
 825                            e.insert(value);
 826                        }
 827                        btree_map::Entry::Occupied(e) => {
 828                            if let (Schema::Object(target), Schema::Object(src)) =
 829                                (e.into_mut(), value)
 830                            {
 831                                merge_schema(target, src);
 832                            }
 833                        }
 834                    }
 835                }
 836            }
 837
 838            overwrite(&mut target.instance_type, source.instance_type);
 839            overwrite(&mut target.string, source.string);
 840            overwrite(&mut target.number, source.number);
 841            overwrite(&mut target.reference, source.reference);
 842            overwrite(&mut target.array, source.array);
 843            overwrite(&mut target.enum_values, source.enum_values);
 844
 845            fn overwrite<T>(target: &mut Option<T>, source: Option<T>) {
 846                if let Some(source) = source {
 847                    *target = Some(source);
 848                }
 849            }
 850        }
 851
 852        for release_stage in ["dev", "nightly", "stable", "preview"] {
 853            let schema = combined_schema.schema.clone();
 854            combined_schema
 855                .schema
 856                .object()
 857                .properties
 858                .insert(release_stage.to_string(), schema.into());
 859        }
 860
 861        serde_json::to_value(&combined_schema).unwrap()
 862    }
 863
 864    fn recompute_values(
 865        &mut self,
 866        changed_local_path: Option<(WorktreeId, &Path)>,
 867        cx: &mut App,
 868    ) -> std::result::Result<(), InvalidSettingsError> {
 869        // Reload the global and local values for every setting.
 870        let mut project_settings_stack = Vec::<DeserializedSetting>::new();
 871        let mut paths_stack = Vec::<Option<(WorktreeId, &Path)>>::new();
 872        for setting_value in self.setting_values.values_mut() {
 873            let default_settings = setting_value
 874                .deserialize_setting(&self.raw_default_settings)
 875                .map_err(|e| InvalidSettingsError::DefaultSettings {
 876                    message: e.to_string(),
 877                })?;
 878
 879            let extension_settings = setting_value
 880                .deserialize_setting(&self.raw_extension_settings)
 881                .log_err();
 882
 883            let user_settings = match setting_value.deserialize_setting(&self.raw_user_settings) {
 884                Ok(settings) => Some(settings),
 885                Err(error) => {
 886                    return Err(InvalidSettingsError::UserSettings {
 887                        message: error.to_string(),
 888                    });
 889                }
 890            };
 891
 892            let server_settings = self
 893                .raw_server_settings
 894                .as_ref()
 895                .and_then(|setting| setting_value.deserialize_setting(setting).log_err());
 896
 897            let mut release_channel_settings = None;
 898            if let Some(release_settings) = &self
 899                .raw_user_settings
 900                .get(release_channel::RELEASE_CHANNEL.dev_name())
 901            {
 902                if let Some(release_settings) = setting_value
 903                    .deserialize_setting(release_settings)
 904                    .log_err()
 905                {
 906                    release_channel_settings = Some(release_settings);
 907                }
 908            }
 909
 910            // If the global settings file changed, reload the global value for the field.
 911            if changed_local_path.is_none() {
 912                if let Some(value) = setting_value
 913                    .load_setting(
 914                        SettingsSources {
 915                            default: &default_settings,
 916                            extensions: extension_settings.as_ref(),
 917                            user: user_settings.as_ref(),
 918                            release_channel: release_channel_settings.as_ref(),
 919                            server: server_settings.as_ref(),
 920                            project: &[],
 921                        },
 922                        cx,
 923                    )
 924                    .log_err()
 925                {
 926                    setting_value.set_global_value(value);
 927                }
 928            }
 929
 930            // Reload the local values for the setting.
 931            paths_stack.clear();
 932            project_settings_stack.clear();
 933            for ((root_id, directory_path), local_settings) in &self.raw_local_settings {
 934                // Build a stack of all of the local values for that setting.
 935                while let Some(prev_entry) = paths_stack.last() {
 936                    if let Some((prev_root_id, prev_path)) = prev_entry {
 937                        if root_id != prev_root_id || !directory_path.starts_with(prev_path) {
 938                            paths_stack.pop();
 939                            project_settings_stack.pop();
 940                            continue;
 941                        }
 942                    }
 943                    break;
 944                }
 945
 946                match setting_value.deserialize_setting(local_settings) {
 947                    Ok(local_settings) => {
 948                        paths_stack.push(Some((*root_id, directory_path.as_ref())));
 949                        project_settings_stack.push(local_settings);
 950
 951                        // If a local settings file changed, then avoid recomputing local
 952                        // settings for any path outside of that directory.
 953                        if changed_local_path.map_or(
 954                            false,
 955                            |(changed_root_id, changed_local_path)| {
 956                                *root_id != changed_root_id
 957                                    || !directory_path.starts_with(changed_local_path)
 958                            },
 959                        ) {
 960                            continue;
 961                        }
 962
 963                        if let Some(value) = setting_value
 964                            .load_setting(
 965                                SettingsSources {
 966                                    default: &default_settings,
 967                                    extensions: extension_settings.as_ref(),
 968                                    user: user_settings.as_ref(),
 969                                    release_channel: release_channel_settings.as_ref(),
 970                                    server: server_settings.as_ref(),
 971                                    project: &project_settings_stack.iter().collect::<Vec<_>>(),
 972                                },
 973                                cx,
 974                            )
 975                            .log_err()
 976                        {
 977                            setting_value.set_local_value(*root_id, directory_path.clone(), value);
 978                        }
 979                    }
 980                    Err(error) => {
 981                        return Err(InvalidSettingsError::LocalSettings {
 982                            path: directory_path.join(local_settings_file_relative_path()),
 983                            message: error.to_string(),
 984                        });
 985                    }
 986                }
 987            }
 988        }
 989        Ok(())
 990    }
 991
 992    pub fn editorconfig_properties(
 993        &self,
 994        for_worktree: WorktreeId,
 995        for_path: &Path,
 996    ) -> Option<EditorconfigProperties> {
 997        let mut properties = EditorconfigProperties::new();
 998
 999        for (directory_with_config, _, parsed_editorconfig) in
1000            self.local_editorconfig_settings(for_worktree)
1001        {
1002            if !for_path.starts_with(&directory_with_config) {
1003                properties.use_fallbacks();
1004                return Some(properties);
1005            }
1006            let parsed_editorconfig = parsed_editorconfig?;
1007            if parsed_editorconfig.is_root {
1008                properties = EditorconfigProperties::new();
1009            }
1010            for section in parsed_editorconfig.sections {
1011                section.apply_to(&mut properties, for_path).log_err()?;
1012            }
1013        }
1014
1015        properties.use_fallbacks();
1016        Some(properties)
1017    }
1018}
1019
1020#[derive(Debug, Clone, PartialEq)]
1021pub enum InvalidSettingsError {
1022    LocalSettings { path: PathBuf, message: String },
1023    UserSettings { message: String },
1024    ServerSettings { message: String },
1025    DefaultSettings { message: String },
1026    Editorconfig { path: PathBuf, message: String },
1027    Tasks { path: PathBuf, message: String },
1028}
1029
1030impl std::fmt::Display for InvalidSettingsError {
1031    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1032        match self {
1033            InvalidSettingsError::LocalSettings { message, .. }
1034            | InvalidSettingsError::UserSettings { message }
1035            | InvalidSettingsError::ServerSettings { message }
1036            | InvalidSettingsError::DefaultSettings { message }
1037            | InvalidSettingsError::Tasks { message, .. }
1038            | InvalidSettingsError::Editorconfig { message, .. } => {
1039                write!(f, "{message}")
1040            }
1041        }
1042    }
1043}
1044impl std::error::Error for InvalidSettingsError {}
1045
1046impl Debug for SettingsStore {
1047    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1048        f.debug_struct("SettingsStore")
1049            .field(
1050                "types",
1051                &self
1052                    .setting_values
1053                    .values()
1054                    .map(|value| value.setting_type_name())
1055                    .collect::<Vec<_>>(),
1056            )
1057            .field("default_settings", &self.raw_default_settings)
1058            .field("user_settings", &self.raw_user_settings)
1059            .field("local_settings", &self.raw_local_settings)
1060            .finish_non_exhaustive()
1061    }
1062}
1063
1064impl<T: Settings> AnySettingValue for SettingValue<T> {
1065    fn key(&self) -> Option<&'static str> {
1066        T::KEY
1067    }
1068
1069    fn setting_type_name(&self) -> &'static str {
1070        type_name::<T>()
1071    }
1072
1073    fn load_setting(
1074        &self,
1075        values: SettingsSources<DeserializedSetting>,
1076        cx: &mut App,
1077    ) -> Result<Box<dyn Any>> {
1078        Ok(Box::new(T::load(
1079            SettingsSources {
1080                default: values.default.0.downcast_ref::<T::FileContent>().unwrap(),
1081                extensions: values
1082                    .extensions
1083                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1084                user: values
1085                    .user
1086                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1087                release_channel: values
1088                    .release_channel
1089                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1090                server: values
1091                    .server
1092                    .map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
1093                project: values
1094                    .project
1095                    .iter()
1096                    .map(|value| value.0.downcast_ref().unwrap())
1097                    .collect::<SmallVec<[_; 3]>>()
1098                    .as_slice(),
1099            },
1100            cx,
1101        )?))
1102    }
1103
1104    fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result<DeserializedSetting> {
1105        if let Some(key) = T::KEY {
1106            if let Some(value) = json.get(key) {
1107                json = value;
1108            } else {
1109                let value = T::FileContent::default();
1110                return Ok(DeserializedSetting(Box::new(value)));
1111            }
1112        }
1113        let value = T::FileContent::deserialize(json)?;
1114        Ok(DeserializedSetting(Box::new(value)))
1115    }
1116
1117    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1118        if let Some(SettingsLocation { worktree_id, path }) = path {
1119            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1120                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1121                    return value;
1122                }
1123            }
1124        }
1125        self.global_value
1126            .as_ref()
1127            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1128    }
1129
1130    fn set_global_value(&mut self, value: Box<dyn Any>) {
1131        self.global_value = Some(*value.downcast().unwrap());
1132    }
1133
1134    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>) {
1135        let value = *value.downcast().unwrap();
1136        match self
1137            .local_values
1138            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1139        {
1140            Ok(ix) => self.local_values[ix].2 = value,
1141            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1142        }
1143    }
1144
1145    fn json_schema(
1146        &self,
1147        generator: &mut SchemaGenerator,
1148        params: &SettingsJsonSchemaParams,
1149        cx: &App,
1150    ) -> RootSchema {
1151        T::json_schema(generator, params, cx)
1152    }
1153}
1154
1155fn update_value_in_json_text<'a>(
1156    text: &mut String,
1157    key_path: &mut Vec<&'a str>,
1158    tab_size: usize,
1159    old_value: &'a serde_json::Value,
1160    new_value: &'a serde_json::Value,
1161    preserved_keys: &[&str],
1162    edits: &mut Vec<(Range<usize>, String)>,
1163) {
1164    // If the old and new values are both objects, then compare them key by key,
1165    // preserving the comments and formatting of the unchanged parts. Otherwise,
1166    // replace the old value with the new value.
1167    if let (serde_json::Value::Object(old_object), serde_json::Value::Object(new_object)) =
1168        (old_value, new_value)
1169    {
1170        for (key, old_sub_value) in old_object.iter() {
1171            key_path.push(key);
1172            let new_sub_value = new_object.get(key).unwrap_or(&serde_json::Value::Null);
1173            update_value_in_json_text(
1174                text,
1175                key_path,
1176                tab_size,
1177                old_sub_value,
1178                new_sub_value,
1179                preserved_keys,
1180                edits,
1181            );
1182            key_path.pop();
1183        }
1184        for (key, new_sub_value) in new_object.iter() {
1185            key_path.push(key);
1186            if !old_object.contains_key(key) {
1187                update_value_in_json_text(
1188                    text,
1189                    key_path,
1190                    tab_size,
1191                    &serde_json::Value::Null,
1192                    new_sub_value,
1193                    preserved_keys,
1194                    edits,
1195                );
1196            }
1197            key_path.pop();
1198        }
1199    } else if key_path
1200        .last()
1201        .map_or(false, |key| preserved_keys.contains(key))
1202        || old_value != new_value
1203    {
1204        let mut new_value = new_value.clone();
1205        if let Some(new_object) = new_value.as_object_mut() {
1206            new_object.retain(|_, v| !v.is_null());
1207        }
1208        let (range, replacement) = replace_value_in_json_text(text, key_path, tab_size, &new_value);
1209        text.replace_range(range.clone(), &replacement);
1210        edits.push((range, replacement));
1211    }
1212}
1213
1214fn replace_value_in_json_text(
1215    text: &str,
1216    key_path: &[&str],
1217    tab_size: usize,
1218    new_value: &serde_json::Value,
1219) -> (Range<usize>, String) {
1220    static PAIR_QUERY: LazyLock<Query> = LazyLock::new(|| {
1221        Query::new(
1222            &tree_sitter_json::LANGUAGE.into(),
1223            "(pair key: (string) @key value: (_) @value)",
1224        )
1225        .expect("Failed to create PAIR_QUERY")
1226    });
1227
1228    let mut parser = tree_sitter::Parser::new();
1229    parser
1230        .set_language(&tree_sitter_json::LANGUAGE.into())
1231        .unwrap();
1232    let syntax_tree = parser.parse(text, None).unwrap();
1233
1234    let mut cursor = tree_sitter::QueryCursor::new();
1235
1236    let mut depth = 0;
1237    let mut last_value_range = 0..0;
1238    let mut first_key_start = None;
1239    let mut existing_value_range = 0..text.len();
1240    let mut matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes());
1241    while let Some(mat) = matches.next() {
1242        if mat.captures.len() != 2 {
1243            continue;
1244        }
1245
1246        let key_range = mat.captures[0].node.byte_range();
1247        let value_range = mat.captures[1].node.byte_range();
1248
1249        // Don't enter sub objects until we find an exact
1250        // match for the current keypath
1251        if last_value_range.contains_inclusive(&value_range) {
1252            continue;
1253        }
1254
1255        last_value_range = value_range.clone();
1256
1257        if key_range.start > existing_value_range.end {
1258            break;
1259        }
1260
1261        first_key_start.get_or_insert(key_range.start);
1262
1263        let found_key = text
1264            .get(key_range.clone())
1265            .map(|key_text| {
1266                depth < key_path.len() && key_text == format!("\"{}\"", key_path[depth])
1267            })
1268            .unwrap_or(false);
1269
1270        if found_key {
1271            existing_value_range = value_range;
1272            // Reset last value range when increasing in depth
1273            last_value_range = existing_value_range.start..existing_value_range.start;
1274            depth += 1;
1275
1276            if depth == key_path.len() {
1277                break;
1278            }
1279
1280            first_key_start = None;
1281        }
1282    }
1283
1284    // We found the exact key we want, insert the new value
1285    if depth == key_path.len() {
1286        let new_val = to_pretty_json(&new_value, tab_size, tab_size * depth);
1287        (existing_value_range, new_val)
1288    } else {
1289        // We have key paths, construct the sub objects
1290        let new_key = key_path[depth];
1291
1292        // We don't have the key, construct the nested objects
1293        let mut new_value = serde_json::to_value(new_value).unwrap();
1294        for key in key_path[(depth + 1)..].iter().rev() {
1295            new_value = serde_json::json!({ key.to_string(): new_value });
1296        }
1297
1298        if let Some(first_key_start) = first_key_start {
1299            let mut row = 0;
1300            let mut column = 0;
1301            for (ix, char) in text.char_indices() {
1302                if ix == first_key_start {
1303                    break;
1304                }
1305                if char == '\n' {
1306                    row += 1;
1307                    column = 0;
1308                } else {
1309                    column += char.len_utf8();
1310                }
1311            }
1312
1313            if row > 0 {
1314                // depth is 0 based, but division needs to be 1 based.
1315                let new_val = to_pretty_json(&new_value, column / (depth + 1), column);
1316                let space = ' ';
1317                let content = format!("\"{new_key}\": {new_val},\n{space:width$}", width = column);
1318                (first_key_start..first_key_start, content)
1319            } else {
1320                let new_val = serde_json::to_string(&new_value).unwrap();
1321                let mut content = format!(r#""{new_key}": {new_val},"#);
1322                content.push(' ');
1323                (first_key_start..first_key_start, content)
1324            }
1325        } else {
1326            new_value = serde_json::json!({ new_key.to_string(): new_value });
1327            let indent_prefix_len = 4 * depth;
1328            let mut new_val = to_pretty_json(&new_value, 4, indent_prefix_len);
1329            if depth == 0 {
1330                new_val.push('\n');
1331            }
1332
1333            (existing_value_range, new_val)
1334        }
1335    }
1336}
1337
1338fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len: usize) -> String {
1339    const SPACES: [u8; 32] = [b' '; 32];
1340
1341    debug_assert!(indent_size <= SPACES.len());
1342    debug_assert!(indent_prefix_len <= SPACES.len());
1343
1344    let mut output = Vec::new();
1345    let mut ser = serde_json::Serializer::with_formatter(
1346        &mut output,
1347        serde_json::ser::PrettyFormatter::with_indent(&SPACES[0..indent_size.min(SPACES.len())]),
1348    );
1349
1350    value.serialize(&mut ser).unwrap();
1351    let text = String::from_utf8(output).unwrap();
1352
1353    let mut adjusted_text = String::new();
1354    for (i, line) in text.split('\n').enumerate() {
1355        if i > 0 {
1356            adjusted_text.push_str(str::from_utf8(&SPACES[0..indent_prefix_len]).unwrap());
1357        }
1358        adjusted_text.push_str(line);
1359        adjusted_text.push('\n');
1360    }
1361    adjusted_text.pop();
1362    adjusted_text
1363}
1364
1365pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T> {
1366    Ok(serde_json_lenient::from_str(content)?)
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371    use super::*;
1372    use serde_derive::Deserialize;
1373    use unindent::Unindent;
1374
1375    #[gpui::test]
1376    fn test_settings_store_basic(cx: &mut App) {
1377        let mut store = SettingsStore::new(cx);
1378        store.register_setting::<UserSettings>(cx);
1379        store.register_setting::<TurboSetting>(cx);
1380        store.register_setting::<MultiKeySettings>(cx);
1381        store
1382            .set_default_settings(
1383                r#"{
1384                    "turbo": false,
1385                    "user": {
1386                        "name": "John Doe",
1387                        "age": 30,
1388                        "staff": false
1389                    }
1390                }"#,
1391                cx,
1392            )
1393            .unwrap();
1394
1395        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
1396        assert_eq!(
1397            store.get::<UserSettings>(None),
1398            &UserSettings {
1399                name: "John Doe".to_string(),
1400                age: 30,
1401                staff: false,
1402            }
1403        );
1404        assert_eq!(
1405            store.get::<MultiKeySettings>(None),
1406            &MultiKeySettings {
1407                key1: String::new(),
1408                key2: String::new(),
1409            }
1410        );
1411
1412        store
1413            .set_user_settings(
1414                r#"{
1415                    "turbo": true,
1416                    "user": { "age": 31 },
1417                    "key1": "a"
1418                }"#,
1419                cx,
1420            )
1421            .unwrap();
1422
1423        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(true));
1424        assert_eq!(
1425            store.get::<UserSettings>(None),
1426            &UserSettings {
1427                name: "John Doe".to_string(),
1428                age: 31,
1429                staff: false
1430            }
1431        );
1432
1433        store
1434            .set_local_settings(
1435                WorktreeId::from_usize(1),
1436                Path::new("/root1").into(),
1437                LocalSettingsKind::Settings,
1438                Some(r#"{ "user": { "staff": true } }"#),
1439                cx,
1440            )
1441            .unwrap();
1442        store
1443            .set_local_settings(
1444                WorktreeId::from_usize(1),
1445                Path::new("/root1/subdir").into(),
1446                LocalSettingsKind::Settings,
1447                Some(r#"{ "user": { "name": "Jane Doe" } }"#),
1448                cx,
1449            )
1450            .unwrap();
1451
1452        store
1453            .set_local_settings(
1454                WorktreeId::from_usize(1),
1455                Path::new("/root2").into(),
1456                LocalSettingsKind::Settings,
1457                Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
1458                cx,
1459            )
1460            .unwrap();
1461
1462        assert_eq!(
1463            store.get::<UserSettings>(Some(SettingsLocation {
1464                worktree_id: WorktreeId::from_usize(1),
1465                path: Path::new("/root1/something"),
1466            })),
1467            &UserSettings {
1468                name: "John Doe".to_string(),
1469                age: 31,
1470                staff: true
1471            }
1472        );
1473        assert_eq!(
1474            store.get::<UserSettings>(Some(SettingsLocation {
1475                worktree_id: WorktreeId::from_usize(1),
1476                path: Path::new("/root1/subdir/something")
1477            })),
1478            &UserSettings {
1479                name: "Jane Doe".to_string(),
1480                age: 31,
1481                staff: true
1482            }
1483        );
1484        assert_eq!(
1485            store.get::<UserSettings>(Some(SettingsLocation {
1486                worktree_id: WorktreeId::from_usize(1),
1487                path: Path::new("/root2/something")
1488            })),
1489            &UserSettings {
1490                name: "John Doe".to_string(),
1491                age: 42,
1492                staff: false
1493            }
1494        );
1495        assert_eq!(
1496            store.get::<MultiKeySettings>(Some(SettingsLocation {
1497                worktree_id: WorktreeId::from_usize(1),
1498                path: Path::new("/root2/something")
1499            })),
1500            &MultiKeySettings {
1501                key1: "a".to_string(),
1502                key2: "b".to_string(),
1503            }
1504        );
1505    }
1506
1507    #[gpui::test]
1508    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1509        let mut store = SettingsStore::new(cx);
1510        store
1511            .set_default_settings(
1512                r#"{
1513                    "turbo": true,
1514                    "user": {
1515                        "name": "John Doe",
1516                        "age": 30,
1517                        "staff": false
1518                    },
1519                    "key1": "x"
1520                }"#,
1521                cx,
1522            )
1523            .unwrap();
1524        store
1525            .set_user_settings(r#"{ "turbo": false }"#, cx)
1526            .unwrap();
1527        store.register_setting::<UserSettings>(cx);
1528        store.register_setting::<TurboSetting>(cx);
1529
1530        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
1531        assert_eq!(
1532            store.get::<UserSettings>(None),
1533            &UserSettings {
1534                name: "John Doe".to_string(),
1535                age: 30,
1536                staff: false,
1537            }
1538        );
1539
1540        store.register_setting::<MultiKeySettings>(cx);
1541        assert_eq!(
1542            store.get::<MultiKeySettings>(None),
1543            &MultiKeySettings {
1544                key1: "x".into(),
1545                key2: String::new(),
1546            }
1547        );
1548    }
1549
1550    #[gpui::test]
1551    fn test_setting_store_update(cx: &mut App) {
1552        let mut store = SettingsStore::new(cx);
1553        store.register_setting::<MultiKeySettings>(cx);
1554        store.register_setting::<UserSettings>(cx);
1555        store.register_setting::<LanguageSettings>(cx);
1556
1557        // entries added and updated
1558        check_settings_update::<LanguageSettings>(
1559            &mut store,
1560            r#"{
1561                "languages": {
1562                    "JSON": {
1563                        "language_setting_1": true
1564                    }
1565                }
1566            }"#
1567            .unindent(),
1568            |settings| {
1569                settings
1570                    .languages
1571                    .get_mut("JSON")
1572                    .unwrap()
1573                    .language_setting_1 = Some(false);
1574                settings.languages.insert(
1575                    "Rust".into(),
1576                    LanguageSettingEntry {
1577                        language_setting_2: Some(true),
1578                        ..Default::default()
1579                    },
1580                );
1581            },
1582            r#"{
1583                "languages": {
1584                    "Rust": {
1585                        "language_setting_2": true
1586                    },
1587                    "JSON": {
1588                        "language_setting_1": false
1589                    }
1590                }
1591            }"#
1592            .unindent(),
1593            cx,
1594        );
1595
1596        // weird formatting
1597        check_settings_update::<UserSettings>(
1598            &mut store,
1599            r#"{
1600                "user":   { "age": 36, "name": "Max", "staff": true }
1601            }"#
1602            .unindent(),
1603            |settings| settings.age = Some(37),
1604            r#"{
1605                "user":   { "age": 37, "name": "Max", "staff": true }
1606            }"#
1607            .unindent(),
1608            cx,
1609        );
1610
1611        // single-line formatting, other keys
1612        check_settings_update::<MultiKeySettings>(
1613            &mut store,
1614            r#"{ "one": 1, "two": 2 }"#.unindent(),
1615            |settings| settings.key1 = Some("x".into()),
1616            r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(),
1617            cx,
1618        );
1619
1620        // empty object
1621        check_settings_update::<UserSettings>(
1622            &mut store,
1623            r#"{
1624                "user": {}
1625            }"#
1626            .unindent(),
1627            |settings| settings.age = Some(37),
1628            r#"{
1629                "user": {
1630                    "age": 37
1631                }
1632            }"#
1633            .unindent(),
1634            cx,
1635        );
1636
1637        // no content
1638        check_settings_update::<UserSettings>(
1639            &mut store,
1640            r#""#.unindent(),
1641            |settings| settings.age = Some(37),
1642            r#"{
1643                "user": {
1644                    "age": 37
1645                }
1646            }
1647            "#
1648            .unindent(),
1649            cx,
1650        );
1651
1652        check_settings_update::<UserSettings>(
1653            &mut store,
1654            r#"{
1655            }
1656            "#
1657            .unindent(),
1658            |settings| settings.age = Some(37),
1659            r#"{
1660                "user": {
1661                    "age": 37
1662                }
1663            }
1664            "#
1665            .unindent(),
1666            cx,
1667        );
1668    }
1669
1670    fn check_settings_update<T: Settings>(
1671        store: &mut SettingsStore,
1672        old_json: String,
1673        update: fn(&mut T::FileContent),
1674        expected_new_json: String,
1675        cx: &mut App,
1676    ) {
1677        store.set_user_settings(&old_json, cx).ok();
1678        let edits = store.edits_for_update::<T>(&old_json, update);
1679        let mut new_json = old_json;
1680        for (range, replacement) in edits.into_iter() {
1681            new_json.replace_range(range, &replacement);
1682        }
1683        pretty_assertions::assert_eq!(new_json, expected_new_json);
1684    }
1685
1686    #[derive(Debug, PartialEq, Deserialize)]
1687    struct UserSettings {
1688        name: String,
1689        age: u32,
1690        staff: bool,
1691    }
1692
1693    #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)]
1694    struct UserSettingsJson {
1695        name: Option<String>,
1696        age: Option<u32>,
1697        staff: Option<bool>,
1698    }
1699
1700    impl Settings for UserSettings {
1701        const KEY: Option<&'static str> = Some("user");
1702        type FileContent = UserSettingsJson;
1703
1704        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1705            sources.json_merge()
1706        }
1707    }
1708
1709    #[derive(Debug, Deserialize, PartialEq)]
1710    struct TurboSetting(bool);
1711
1712    impl Settings for TurboSetting {
1713        const KEY: Option<&'static str> = Some("turbo");
1714        type FileContent = Option<bool>;
1715
1716        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1717            sources.json_merge()
1718        }
1719    }
1720
1721    #[derive(Clone, Debug, PartialEq, Deserialize)]
1722    struct MultiKeySettings {
1723        #[serde(default)]
1724        key1: String,
1725        #[serde(default)]
1726        key2: String,
1727    }
1728
1729    #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1730    struct MultiKeySettingsJson {
1731        key1: Option<String>,
1732        key2: Option<String>,
1733    }
1734
1735    impl Settings for MultiKeySettings {
1736        const KEY: Option<&'static str> = None;
1737
1738        type FileContent = MultiKeySettingsJson;
1739
1740        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1741            sources.json_merge()
1742        }
1743    }
1744
1745    #[derive(Debug, Deserialize)]
1746    struct JournalSettings {
1747        pub path: String,
1748        pub hour_format: HourFormat,
1749    }
1750
1751    #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1752    #[serde(rename_all = "snake_case")]
1753    enum HourFormat {
1754        Hour12,
1755        Hour24,
1756    }
1757
1758    #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
1759    struct JournalSettingsJson {
1760        pub path: Option<String>,
1761        pub hour_format: Option<HourFormat>,
1762    }
1763
1764    impl Settings for JournalSettings {
1765        const KEY: Option<&'static str> = Some("journal");
1766
1767        type FileContent = JournalSettingsJson;
1768
1769        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1770            sources.json_merge()
1771        }
1772    }
1773
1774    #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1775    struct LanguageSettings {
1776        #[serde(default)]
1777        languages: HashMap<String, LanguageSettingEntry>,
1778    }
1779
1780    #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1781    struct LanguageSettingEntry {
1782        language_setting_1: Option<bool>,
1783        language_setting_2: Option<bool>,
1784    }
1785
1786    impl Settings for LanguageSettings {
1787        const KEY: Option<&'static str> = None;
1788
1789        type FileContent = Self;
1790
1791        fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1792            sources.json_merge()
1793        }
1794    }
1795}