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