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        dbg!(&new_content.content.title_bar);
 620        update(&mut new_content.content);
 621        dbg!(&new_content.content.title_bar);
 622
 623        let old_value = serde_json::to_value(&old_content).unwrap();
 624        let new_value = serde_json::to_value(new_content).unwrap();
 625        // dbg!(&old_value, &new_value);
 626
 627        let mut key_path = Vec::new();
 628        let mut edits = Vec::new();
 629        let tab_size = self.json_tab_size();
 630        let mut text = text.to_string();
 631        update_value_in_json_text(
 632            &mut text,
 633            &mut key_path,
 634            tab_size,
 635            &old_value,
 636            &new_value,
 637            &[], // todo!() is this still needed?
 638            &mut edits,
 639        );
 640        edits
 641    }
 642
 643    pub fn json_tab_size(&self) -> usize {
 644        2
 645    }
 646
 647    /// Sets the default settings via a JSON string.
 648    ///
 649    /// The string should contain a JSON object with a default value for every setting.
 650    pub fn set_default_settings(
 651        &mut self,
 652        default_settings_content: &str,
 653        cx: &mut App,
 654    ) -> Result<()> {
 655        self.default_settings = parse_json_with_comments(default_settings_content)?;
 656        self.recompute_values(None, cx)?;
 657        Ok(())
 658    }
 659
 660    /// Sets the user settings via a JSON string.
 661    pub fn set_user_settings(&mut self, user_settings_content: &str, cx: &mut App) -> Result<()> {
 662        let settings: UserSettingsContent = if user_settings_content.is_empty() {
 663            parse_json_with_comments("{}")?
 664        } else {
 665            parse_json_with_comments(user_settings_content)?
 666        };
 667
 668        self.user_settings = Some(settings);
 669        self.recompute_values(None, cx)?;
 670        Ok(())
 671    }
 672
 673    /// Sets the global settings via a JSON string.
 674    pub fn set_global_settings(
 675        &mut self,
 676        global_settings_content: &str,
 677        cx: &mut App,
 678    ) -> Result<()> {
 679        let settings: SettingsContent = if global_settings_content.is_empty() {
 680            parse_json_with_comments("{}")?
 681        } else {
 682            parse_json_with_comments(global_settings_content)?
 683        };
 684
 685        self.global_settings = Some(settings);
 686        self.recompute_values(None, cx)?;
 687        Ok(())
 688    }
 689
 690    pub fn set_server_settings(
 691        &mut self,
 692        server_settings_content: &str,
 693        cx: &mut App,
 694    ) -> Result<()> {
 695        let settings: Option<ServerSettingsContent> = if server_settings_content.is_empty() {
 696            None
 697        } else {
 698            parse_json_with_comments(server_settings_content)?
 699        };
 700
 701        // Rewrite the server settings into a content type
 702        self.server_settings = settings.map(|settings| SettingsContent {
 703            project: settings.project,
 704            ..Default::default()
 705        });
 706
 707        self.recompute_values(None, cx)?;
 708        Ok(())
 709    }
 710
 711    /// Add or remove a set of local settings via a JSON string.
 712    pub fn set_local_settings(
 713        &mut self,
 714        root_id: WorktreeId,
 715        directory_path: Arc<Path>,
 716        kind: LocalSettingsKind,
 717        settings_content: Option<&str>,
 718        cx: &mut App,
 719    ) -> std::result::Result<(), InvalidSettingsError> {
 720        let mut zed_settings_changed = false;
 721        match (
 722            kind,
 723            settings_content
 724                .map(|content| content.trim())
 725                .filter(|content| !content.is_empty()),
 726        ) {
 727            (LocalSettingsKind::Tasks, _) => {
 728                return Err(InvalidSettingsError::Tasks {
 729                    message: "Attempted to submit tasks into the settings store".to_string(),
 730                    path: directory_path.join(task_file_name()),
 731                });
 732            }
 733            (LocalSettingsKind::Debug, _) => {
 734                return Err(InvalidSettingsError::Debug {
 735                    message: "Attempted to submit debugger config into the settings store"
 736                        .to_string(),
 737                    path: directory_path.join(task_file_name()),
 738                });
 739            }
 740            (LocalSettingsKind::Settings, None) => {
 741                zed_settings_changed = self
 742                    .local_settings
 743                    .remove(&(root_id, directory_path.clone()))
 744                    .is_some()
 745            }
 746            (LocalSettingsKind::Editorconfig, None) => {
 747                self.raw_editorconfig_settings
 748                    .remove(&(root_id, directory_path.clone()));
 749            }
 750            (LocalSettingsKind::Settings, Some(settings_contents)) => {
 751                let new_settings = parse_json_with_comments::<ProjectSettingsContent>(
 752                    settings_contents,
 753                )
 754                .map_err(|e| InvalidSettingsError::LocalSettings {
 755                    path: directory_path.join(local_settings_file_relative_path()),
 756                    message: e.to_string(),
 757                })?;
 758                match self.local_settings.entry((root_id, directory_path.clone())) {
 759                    btree_map::Entry::Vacant(v) => {
 760                        v.insert(SettingsContent {
 761                            project: new_settings,
 762                            ..Default::default()
 763                        });
 764                        zed_settings_changed = true;
 765                    }
 766                    btree_map::Entry::Occupied(mut o) => {
 767                        if &o.get().project != &new_settings {
 768                            o.insert(SettingsContent {
 769                                project: new_settings,
 770                                ..Default::default()
 771                            });
 772                            zed_settings_changed = true;
 773                        }
 774                    }
 775                }
 776            }
 777            (LocalSettingsKind::Editorconfig, Some(editorconfig_contents)) => {
 778                match self
 779                    .raw_editorconfig_settings
 780                    .entry((root_id, directory_path.clone()))
 781                {
 782                    btree_map::Entry::Vacant(v) => match editorconfig_contents.parse() {
 783                        Ok(new_contents) => {
 784                            v.insert((editorconfig_contents.to_owned(), Some(new_contents)));
 785                        }
 786                        Err(e) => {
 787                            v.insert((editorconfig_contents.to_owned(), None));
 788                            return Err(InvalidSettingsError::Editorconfig {
 789                                message: e.to_string(),
 790                                path: directory_path.join(EDITORCONFIG_NAME),
 791                            });
 792                        }
 793                    },
 794                    btree_map::Entry::Occupied(mut o) => {
 795                        if o.get().0 != editorconfig_contents {
 796                            match editorconfig_contents.parse() {
 797                                Ok(new_contents) => {
 798                                    o.insert((
 799                                        editorconfig_contents.to_owned(),
 800                                        Some(new_contents),
 801                                    ));
 802                                }
 803                                Err(e) => {
 804                                    o.insert((editorconfig_contents.to_owned(), None));
 805                                    return Err(InvalidSettingsError::Editorconfig {
 806                                        message: e.to_string(),
 807                                        path: directory_path.join(EDITORCONFIG_NAME),
 808                                    });
 809                                }
 810                            }
 811                        }
 812                    }
 813                }
 814            }
 815        };
 816
 817        if zed_settings_changed {
 818            self.recompute_values(Some((root_id, &directory_path)), cx)?;
 819        }
 820        Ok(())
 821    }
 822
 823    pub fn set_extension_settings(
 824        &mut self,
 825        content: ExtensionsSettingsContent,
 826        cx: &mut App,
 827    ) -> Result<()> {
 828        self.extension_settings = Some(SettingsContent {
 829            project: ProjectSettingsContent {
 830                all_languages: content.all_languages,
 831            },
 832            ..Default::default()
 833        });
 834        self.recompute_values(None, cx)?;
 835        Ok(())
 836    }
 837
 838    /// Add or remove a set of local settings via a JSON string.
 839    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
 840        self.local_settings
 841            .retain(|(worktree_id, _), _| worktree_id != &root_id);
 842        self.recompute_values(Some((root_id, "".as_ref())), cx)?;
 843        Ok(())
 844    }
 845
 846    pub fn local_settings(
 847        &self,
 848        root_id: WorktreeId,
 849    ) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
 850        self.local_settings
 851            .range(
 852                (root_id, Path::new("").into())
 853                    ..(
 854                        WorktreeId::from_usize(root_id.to_usize() + 1),
 855                        Path::new("").into(),
 856                    ),
 857            )
 858            .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))
 859    }
 860
 861    pub fn local_editorconfig_settings(
 862        &self,
 863        root_id: WorktreeId,
 864    ) -> impl '_ + Iterator<Item = (Arc<Path>, String, Option<Editorconfig>)> {
 865        self.raw_editorconfig_settings
 866            .range(
 867                (root_id, Path::new("").into())
 868                    ..(
 869                        WorktreeId::from_usize(root_id.to_usize() + 1),
 870                        Path::new("").into(),
 871                    ),
 872            )
 873            .map(|((_, path), (content, parsed_content))| {
 874                (path.clone(), content.clone(), parsed_content.clone())
 875            })
 876    }
 877
 878    pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams, cx: &App) -> Value {
 879        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
 880            .with_transform(DefaultDenyUnknownFields)
 881            .into_generator();
 882
 883        let schema = UserSettingsContent::json_schema(&mut generator);
 884
 885        // add schemas which are determined at runtime
 886        for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
 887            (parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
 888        }
 889
 890        schema.to_value()
 891    }
 892
 893    fn recompute_values(
 894        &mut self,
 895        changed_local_path: Option<(WorktreeId, &Path)>,
 896        cx: &mut App,
 897    ) -> std::result::Result<(), InvalidSettingsError> {
 898        // Reload the global and local values for every setting.
 899        let mut project_settings_stack = Vec::<&SettingsContent>::new();
 900        let mut paths_stack = Vec::<Option<(WorktreeId, &Path)>>::new();
 901
 902        let mut refinements = Vec::default();
 903
 904        if let Some(extension_settings) = self.extension_settings.as_ref() {
 905            refinements.push(extension_settings)
 906        }
 907
 908        if let Some(user_settings) = self.user_settings.as_ref() {
 909            refinements.push(&user_settings.content);
 910            if let Some(release_channel) = user_settings.for_release_channel() {
 911                refinements.push(release_channel)
 912            }
 913            if let Some(os) = user_settings.for_os() {
 914                refinements.push(os)
 915            }
 916            if let Some(profile) = user_settings.for_profile(cx) {
 917                refinements.push(profile)
 918            }
 919        }
 920
 921        if let Some(server_settings) = self.server_settings.as_ref() {
 922            refinements.push(server_settings)
 923        }
 924
 925        for setting_value in self.setting_values.values_mut() {
 926            // If the global settings file changed, reload the global value for the field.
 927            if changed_local_path.is_none() {
 928                let mut value = setting_value.from_file(&self.default_settings, cx).unwrap();
 929                setting_value.refine(value.as_mut(), &refinements, cx);
 930                setting_value.set_global_value(value);
 931            }
 932
 933            // Reload the local values for the setting.
 934            paths_stack.clear();
 935            project_settings_stack.clear();
 936            for ((root_id, directory_path), local_settings) in &self.local_settings {
 937                // Build a stack of all of the local values for that setting.
 938                while let Some(prev_entry) = paths_stack.last() {
 939                    if let Some((prev_root_id, prev_path)) = prev_entry
 940                        && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
 941                    {
 942                        paths_stack.pop();
 943                        project_settings_stack.pop();
 944                        continue;
 945                    }
 946                    break;
 947                }
 948
 949                // NOTE: this kind of condition existing in the old code too,
 950                // but is there a problem when a setting is removed from a file?
 951                if setting_value.from_file(local_settings, cx).is_some() {
 952                    paths_stack.push(Some((*root_id, directory_path.as_ref())));
 953                    project_settings_stack.push(local_settings);
 954
 955                    // If a local settings file changed, then avoid recomputing local
 956                    // settings for any path outside of that directory.
 957                    if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
 958                        *root_id != changed_root_id
 959                            || !directory_path.starts_with(changed_local_path)
 960                    }) {
 961                        continue;
 962                    }
 963
 964                    let mut value = setting_value.from_file(&self.default_settings, cx).unwrap();
 965                    setting_value.refine(value.as_mut(), &refinements, cx);
 966                    setting_value.refine(value.as_mut(), &project_settings_stack, cx);
 967                    setting_value.set_local_value(*root_id, directory_path.clone(), value);
 968                }
 969            }
 970        }
 971        Ok(())
 972    }
 973
 974    pub fn editorconfig_properties(
 975        &self,
 976        for_worktree: WorktreeId,
 977        for_path: &Path,
 978    ) -> Option<EditorconfigProperties> {
 979        let mut properties = EditorconfigProperties::new();
 980
 981        for (directory_with_config, _, parsed_editorconfig) in
 982            self.local_editorconfig_settings(for_worktree)
 983        {
 984            if !for_path.starts_with(&directory_with_config) {
 985                properties.use_fallbacks();
 986                return Some(properties);
 987            }
 988            let parsed_editorconfig = parsed_editorconfig?;
 989            if parsed_editorconfig.is_root {
 990                properties = EditorconfigProperties::new();
 991            }
 992            for section in parsed_editorconfig.sections {
 993                section.apply_to(&mut properties, for_path).log_err()?;
 994            }
 995        }
 996
 997        properties.use_fallbacks();
 998        Some(properties)
 999    }
1000}
1001
1002#[derive(Debug, Clone, PartialEq)]
1003pub enum InvalidSettingsError {
1004    LocalSettings { path: PathBuf, message: String },
1005    UserSettings { message: String },
1006    ServerSettings { message: String },
1007    DefaultSettings { message: String },
1008    Editorconfig { path: PathBuf, message: String },
1009    Tasks { path: PathBuf, message: String },
1010    Debug { path: PathBuf, message: String },
1011}
1012
1013impl std::fmt::Display for InvalidSettingsError {
1014    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1015        match self {
1016            InvalidSettingsError::LocalSettings { message, .. }
1017            | InvalidSettingsError::UserSettings { message }
1018            | InvalidSettingsError::ServerSettings { message }
1019            | InvalidSettingsError::DefaultSettings { message }
1020            | InvalidSettingsError::Tasks { message, .. }
1021            | InvalidSettingsError::Editorconfig { message, .. }
1022            | InvalidSettingsError::Debug { message, .. } => {
1023                write!(f, "{message}")
1024            }
1025        }
1026    }
1027}
1028impl std::error::Error for InvalidSettingsError {}
1029
1030impl Debug for SettingsStore {
1031    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1032        f.debug_struct("SettingsStore")
1033            .field(
1034                "types",
1035                &self
1036                    .setting_values
1037                    .values()
1038                    .map(|value| value.setting_type_name())
1039                    .collect::<Vec<_>>(),
1040            )
1041            .field("default_settings", &self.default_settings)
1042            .field("user_settings", &self.user_settings)
1043            .field("local_settings", &self.local_settings)
1044            .finish_non_exhaustive()
1045    }
1046}
1047
1048impl<T: Settings> AnySettingValue for SettingValue<T> {
1049    fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option<Box<dyn Any>> {
1050        (type_name::<T>(), TypeId::of::<T>());
1051        T::from_file(s, cx).map(|result| Box::new(result) as _)
1052    }
1053
1054    fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent], cx: &mut App) {
1055        (type_name::<T>(), TypeId::of::<T>());
1056        let value = value.downcast_mut::<T>().unwrap();
1057        for refinement in refinements {
1058            value.refine(refinement, cx)
1059        }
1060    }
1061
1062    fn setting_type_name(&self) -> &'static str {
1063        type_name::<T>()
1064    }
1065
1066    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)> {
1067        self.local_values
1068            .iter()
1069            .map(|(id, path, value)| (*id, path.clone(), value as _))
1070            .collect()
1071    }
1072
1073    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1074        if let Some(SettingsLocation { worktree_id, path }) = path {
1075            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1076                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1077                    return value;
1078                }
1079            }
1080        }
1081
1082        self.global_value
1083            .as_ref()
1084            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1085    }
1086
1087    fn set_global_value(&mut self, value: Box<dyn Any>) {
1088        self.global_value = Some(*value.downcast().unwrap());
1089    }
1090
1091    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>) {
1092        let value = *value.downcast().unwrap();
1093        match self
1094            .local_values
1095            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1096        {
1097            Ok(ix) => self.local_values[ix].2 = value,
1098            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1099        }
1100    }
1101
1102    fn import_from_vscode(
1103        &self,
1104        vscode_settings: &VsCodeSettings,
1105        settings_content: &mut SettingsContent,
1106    ) {
1107        T::import_from_vscode(vscode_settings, settings_content);
1108    }
1109
1110    fn settings_ui_item(&self) -> SettingsUiEntry {
1111        todo!()
1112        // <<T as Settings>::FileContent as SettingsUi>::settings_ui_entry()
1113    }
1114}
1115
1116#[cfg(test)]
1117mod tests {
1118    use crate::{
1119        TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, default_settings,
1120        settings_content::LanguageSettingsContent, test_settings,
1121    };
1122
1123    use super::*;
1124    // This is so the SettingsUi macro can still work properly
1125    use crate as settings;
1126    use serde::Deserialize;
1127    use settings_ui_macros::{SettingsKey, SettingsUi};
1128    use unindent::Unindent;
1129    use util::MergeFrom;
1130
1131    #[derive(Debug, PartialEq)]
1132    struct AutoUpdateSetting {
1133        auto_update: bool,
1134    }
1135
1136    impl Settings for AutoUpdateSetting {
1137        fn from_file(content: &SettingsContent, _: &mut App) -> Option<Self> {
1138            content
1139                .auto_update
1140                .map(|auto_update| AutoUpdateSetting { auto_update })
1141        }
1142
1143        fn refine(&mut self, content: &SettingsContent, _: &mut App) {
1144            if let Some(auto_update) = content.auto_update {
1145                self.auto_update = auto_update;
1146            }
1147        }
1148
1149        fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {}
1150    }
1151
1152    #[derive(Debug, PartialEq)]
1153    struct TitleBarSettings {
1154        show: TitleBarVisibilityContent,
1155        show_branch_name: bool,
1156    }
1157
1158    impl Settings for TitleBarSettings {
1159        fn from_file(content: &SettingsContent, _: &mut App) -> Option<Self> {
1160            let content = content.title_bar.clone()?;
1161            Some(TitleBarSettings {
1162                show: content.show?,
1163                show_branch_name: content.show_branch_name?,
1164            })
1165        }
1166
1167        fn refine(&mut self, content: &SettingsContent, _: &mut App) {
1168            let Some(content) = content.title_bar.as_ref() else {
1169                return;
1170            };
1171            self.show.merge_from(&content.show)
1172        }
1173
1174        fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {}
1175    }
1176
1177    #[gpui::test]
1178    fn test_settings_store_basic(cx: &mut App) {
1179        let mut store = SettingsStore::new(cx, &default_settings());
1180        store.register_setting::<AutoUpdateSetting>(cx);
1181        store.register_setting::<TitleBarSettings>(cx);
1182        // store.register_setting::<MultiKeySettings>(cx);
1183
1184        assert_eq!(
1185            store.get::<AutoUpdateSetting>(None),
1186            &AutoUpdateSetting { auto_update: true }
1187        );
1188        assert_eq!(
1189            store.get::<TitleBarSettings>(None).show,
1190            TitleBarVisibilityContent::Always
1191        );
1192
1193        store
1194            .set_user_settings(
1195                r#"{
1196                    "auto_update": false,
1197                    "title_bar": {
1198                      "show": "never"
1199                    }
1200                }"#,
1201                cx,
1202            )
1203            .unwrap();
1204
1205        assert_eq!(
1206            store.get::<AutoUpdateSetting>(None),
1207            &AutoUpdateSetting { auto_update: false }
1208        );
1209        assert_eq!(
1210            store.get::<TitleBarSettings>(None).show,
1211            TitleBarVisibilityContent::Never
1212        );
1213
1214        // todo!()
1215        // store
1216        //     .set_local_settings(
1217        //         WorktreeId::from_usize(1),
1218        //         Path::new("/root1").into(),
1219        //         LocalSettingsKind::Settings,
1220        //         Some(r#"{ "user": { "staff": true } }"#),
1221        //         cx,
1222        //     )
1223        //     .unwrap();
1224        // store
1225        //     .set_local_settings(
1226        //         WorktreeId::from_usize(1),
1227        //         Path::new("/root1/subdir").into(),
1228        //         LocalSettingsKind::Settings,
1229        //         Some(r#"{ "user": { "name": "Jane Doe" } }"#),
1230        //         cx,
1231        //     )
1232        //     .unwrap();
1233
1234        // store
1235        //     .set_local_settings(
1236        //         WorktreeId::from_usize(1),
1237        //         Path::new("/root2").into(),
1238        //         LocalSettingsKind::Settings,
1239        //         Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
1240        //         cx,
1241        //     )
1242        //     .unwrap();
1243
1244        // assert_eq!(
1245        //     store.get::<UserSettings>(Some(SettingsLocation {
1246        //         worktree_id: WorktreeId::from_usize(1),
1247        //         path: Path::new("/root1/something"),
1248        //     })),
1249        //     &UserSettings {
1250        //         name: "John Doe".to_string(),
1251        //         age: 31,
1252        //         staff: true
1253        //     }
1254        // );
1255        // assert_eq!(
1256        //     store.get::<UserSettings>(Some(SettingsLocation {
1257        //         worktree_id: WorktreeId::from_usize(1),
1258        //         path: Path::new("/root1/subdir/something")
1259        //     })),
1260        //     &UserSettings {
1261        //         name: "Jane Doe".to_string(),
1262        //         age: 31,
1263        //         staff: true
1264        //     }
1265        // );
1266        // assert_eq!(
1267        //     store.get::<UserSettings>(Some(SettingsLocation {
1268        //         worktree_id: WorktreeId::from_usize(1),
1269        //         path: Path::new("/root2/something")
1270        //     })),
1271        //     &UserSettings {
1272        //         name: "John Doe".to_string(),
1273        //         age: 42,
1274        //         staff: false
1275        //     }
1276        // );
1277        // assert_eq!(
1278        //     store.get::<MultiKeySettings>(Some(SettingsLocation {
1279        //         worktree_id: WorktreeId::from_usize(1),
1280        //         path: Path::new("/root2/something")
1281        //     })),
1282        //     &MultiKeySettings {
1283        //         key1: "a".to_string(),
1284        //         key2: "b".to_string(),
1285        //     }
1286        // );
1287    }
1288
1289    #[gpui::test]
1290    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1291        let mut store = SettingsStore::new(cx, &test_settings());
1292        store
1293            .set_user_settings(r#"{ "auto_update": false }"#, cx)
1294            .unwrap();
1295        store.register_setting::<AutoUpdateSetting>(cx);
1296        store.register_setting::<TitleBarSettings>(cx);
1297
1298        assert_eq!(
1299            store.get::<AutoUpdateSetting>(None),
1300            &AutoUpdateSetting { auto_update: false }
1301        );
1302        assert_eq!(
1303            store.get::<TitleBarSettings>(None).show,
1304            TitleBarVisibilityContent::Always,
1305        );
1306    }
1307
1308    #[track_caller]
1309    fn check_settings_update(
1310        store: &mut SettingsStore,
1311        old_json: String,
1312        update: fn(&mut SettingsContent),
1313        expected_new_json: String,
1314        cx: &mut App,
1315    ) {
1316        store.set_user_settings(&old_json, cx).ok();
1317        let edits = store.edits_for_update(&old_json, update);
1318        dbg!(&edits);
1319        let mut new_json = old_json;
1320        for (range, replacement) in edits.into_iter() {
1321            new_json.replace_range(range, &replacement);
1322        }
1323        pretty_assertions::assert_eq!(new_json, expected_new_json);
1324    }
1325
1326    #[gpui::test]
1327    fn test_setting_store_update(cx: &mut App) {
1328        let mut store = SettingsStore::new(cx, &test_settings());
1329        // store.register_setting::<MultiKeySettings>(cx);
1330        // store.register_setting::<UserSettings>(cx);
1331        // store.register_setting::<LanguageSettings>(cx);
1332
1333        // entries added and updated
1334        check_settings_update(
1335            &mut store,
1336            r#"{
1337                "languages": {
1338                    "JSON": {
1339                        "auto_indent": true
1340                    }
1341                }
1342            }"#
1343            .unindent(),
1344            |settings| {
1345                settings
1346                    .languages_mut()
1347                    .get_mut("JSON")
1348                    .unwrap()
1349                    .auto_indent = Some(false);
1350
1351                settings.languages_mut().insert(
1352                    "Rust".into(),
1353                    LanguageSettingsContent {
1354                        auto_indent: Some(true),
1355                        ..Default::default()
1356                    },
1357                );
1358            },
1359            r#"{
1360                "languages": {
1361                    "Rust": {
1362                        "auto_indent": true
1363                    },
1364                    "JSON": {
1365                        "auto_indent": false
1366                    }
1367                }
1368            }"#
1369            .unindent(),
1370            cx,
1371        );
1372
1373        // entries removed
1374        check_settings_update(
1375            &mut store,
1376            r#"{
1377                "languages": {
1378                    "Rust": {
1379                        "language_setting_2": true
1380                    },
1381                    "JSON": {
1382                        "language_setting_1": false
1383                    }
1384                }
1385            }"#
1386            .unindent(),
1387            |settings| {
1388                settings.languages_mut().remove("JSON").unwrap();
1389            },
1390            r#"{
1391                "languages": {
1392                    "Rust": {
1393                        "language_setting_2": true
1394                    }
1395                }
1396            }"#
1397            .unindent(),
1398            cx,
1399        );
1400
1401        check_settings_update(
1402            &mut store,
1403            r#"{
1404                "languages": {
1405                    "Rust": {
1406                        "language_setting_2": true
1407                    },
1408                    "JSON": {
1409                        "language_setting_1": false
1410                    }
1411                }
1412            }"#
1413            .unindent(),
1414            |settings| {
1415                settings.languages_mut().remove("Rust").unwrap();
1416            },
1417            r#"{
1418                "languages": {
1419                    "JSON": {
1420                        "language_setting_1": false
1421                    }
1422                }
1423            }"#
1424            .unindent(),
1425            cx,
1426        );
1427
1428        // weird formatting
1429        check_settings_update(
1430            &mut store,
1431            r#"{
1432                "title_bar":   { "show": "always", "name": "Max"  }
1433                }"#
1434            .unindent(),
1435            |settings| {
1436                dbg!(&settings.title_bar);
1437                settings.title_bar.as_mut().unwrap().show = Some(TitleBarVisibilityContent::Never);
1438                dbg!(&settings.title_bar);
1439            },
1440            r#"{
1441                "title_bar":   { "show": "never", "name": "Max"  }
1442                }"#
1443            .unindent(),
1444            cx,
1445        );
1446
1447        // single-line formatting, other keys
1448        check_settings_update(
1449            &mut store,
1450            r#"{ "one": 1, "two": 2 }"#.to_owned(),
1451            |settings| settings.auto_update = Some(true),
1452            r#"{ "auto_update": true, "one": 1, "two": 2 }"#.to_owned(),
1453            cx,
1454        );
1455
1456        // empty object
1457        check_settings_update(
1458            &mut store,
1459            r#"{
1460                "title_bar": {}
1461            }"#
1462            .unindent(),
1463            |settings| settings.title_bar.as_mut().unwrap().show_menus = Some(true),
1464            r#"{
1465                "title_bar": {
1466                    "show_menus": true
1467                }
1468            }"#
1469            .unindent(),
1470            cx,
1471        );
1472
1473        // no content
1474        check_settings_update(
1475            &mut store,
1476            r#""#.unindent(),
1477            |settings| {
1478                settings.title_bar = Some(TitleBarSettingsContent {
1479                    show_branch_name: Some(true),
1480                    ..Default::default()
1481                })
1482            },
1483            r#"{
1484                "title_bar": {
1485                    "show_branch_name": true
1486                }
1487            }
1488            "#
1489            .unindent(),
1490            cx,
1491        );
1492
1493        check_settings_update(
1494            &mut store,
1495            r#"{
1496            }
1497            "#
1498            .unindent(),
1499            |settings| settings.title_bar.get_or_insert_default().show_branch_name = Some(true),
1500            r#"{
1501                "title_bar": {
1502                    "show_branch_name": true
1503                }
1504            }
1505            "#
1506            .unindent(),
1507            cx,
1508        );
1509    }
1510
1511    // #[gpui::test]
1512    // fn test_vscode_import(cx: &mut App) {
1513    //     let mut store = SettingsStore::new(cx);
1514    //     store.register_setting::<UserSettings>(cx);
1515    //     store.register_setting::<JournalSettings>(cx);
1516    //     store.register_setting::<LanguageSettings>(cx);
1517    //     store.register_setting::<MultiKeySettings>(cx);
1518
1519    //     // create settings that werent present
1520    //     check_vscode_import(
1521    //         &mut store,
1522    //         r#"{
1523    //         }
1524    //         "#
1525    //         .unindent(),
1526    //         r#" { "user.age": 37 } "#.to_owned(),
1527    //         r#"{
1528    //             "user": {
1529    //                 "age": 37
1530    //             }
1531    //         }
1532    //         "#
1533    //         .unindent(),
1534    //         cx,
1535    //     );
1536
1537    //     // persist settings that were present
1538    //     check_vscode_import(
1539    //         &mut store,
1540    //         r#"{
1541    //             "user": {
1542    //                 "staff": true,
1543    //                 "age": 37
1544    //             }
1545    //         }
1546    //         "#
1547    //         .unindent(),
1548    //         r#"{ "user.age": 42 }"#.to_owned(),
1549    //         r#"{
1550    //             "user": {
1551    //                 "staff": true,
1552    //                 "age": 42
1553    //             }
1554    //         }
1555    //         "#
1556    //         .unindent(),
1557    //         cx,
1558    //     );
1559
1560    //     // don't clobber settings that aren't present in vscode
1561    //     check_vscode_import(
1562    //         &mut store,
1563    //         r#"{
1564    //             "user": {
1565    //                 "staff": true,
1566    //                 "age": 37
1567    //             }
1568    //         }
1569    //         "#
1570    //         .unindent(),
1571    //         r#"{}"#.to_owned(),
1572    //         r#"{
1573    //             "user": {
1574    //                 "staff": true,
1575    //                 "age": 37
1576    //             }
1577    //         }
1578    //         "#
1579    //         .unindent(),
1580    //         cx,
1581    //     );
1582
1583    //     // custom enum
1584    //     check_vscode_import(
1585    //         &mut store,
1586    //         r#"{
1587    //             "journal": {
1588    //             "hour_format": "hour12"
1589    //             }
1590    //         }
1591    //         "#
1592    //         .unindent(),
1593    //         r#"{ "time_format": "24" }"#.to_owned(),
1594    //         r#"{
1595    //             "journal": {
1596    //             "hour_format": "hour24"
1597    //             }
1598    //         }
1599    //         "#
1600    //         .unindent(),
1601    //         cx,
1602    //     );
1603
1604    //     // Multiple keys for one setting
1605    //     check_vscode_import(
1606    //         &mut store,
1607    //         r#"{
1608    //             "key1": "value"
1609    //         }
1610    //         "#
1611    //         .unindent(),
1612    //         r#"{
1613    //             "key_1_first": "hello",
1614    //             "key_1_second": "world"
1615    //         }"#
1616    //         .to_owned(),
1617    //         r#"{
1618    //             "key1": "hello world"
1619    //         }
1620    //         "#
1621    //         .unindent(),
1622    //         cx,
1623    //     );
1624
1625    //     // Merging lists together entries added and updated
1626    //     check_vscode_import(
1627    //         &mut store,
1628    //         r#"{
1629    //             "languages": {
1630    //                 "JSON": {
1631    //                     "language_setting_1": true
1632    //                 },
1633    //                 "Rust": {
1634    //                     "language_setting_2": true
1635    //                 }
1636    //             }
1637    //         }"#
1638    //         .unindent(),
1639    //         r#"{
1640    //             "vscode_languages": [
1641    //                 {
1642    //                     "name": "JavaScript",
1643    //                     "language_setting_1": true
1644    //                 },
1645    //                 {
1646    //                     "name": "Rust",
1647    //                     "language_setting_2": false
1648    //                 }
1649    //             ]
1650    //         }"#
1651    //         .to_owned(),
1652    //         r#"{
1653    //             "languages": {
1654    //                 "JavaScript": {
1655    //                     "language_setting_1": true
1656    //                 },
1657    //                 "JSON": {
1658    //                     "language_setting_1": true
1659    //                 },
1660    //                 "Rust": {
1661    //                     "language_setting_2": false
1662    //                 }
1663    //             }
1664    //         }"#
1665    //         .unindent(),
1666    //         cx,
1667    //     );
1668    // }
1669
1670    // fn check_vscode_import(
1671    //     store: &mut SettingsStore,
1672    //     old: String,
1673    //     vscode: String,
1674    //     expected: String,
1675    //     cx: &mut App,
1676    // ) {
1677    //     store.set_user_settings(&old, cx).ok();
1678    //     let new = store.get_vscode_edits(
1679    //         old,
1680    //         &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
1681    //     );
1682    //     pretty_assertions::assert_eq!(new, expected);
1683    // }
1684
1685    // #[derive(Debug, PartialEq, Deserialize, SettingsUi)]
1686    // struct UserSettings {
1687    //     name: String,
1688    //     age: u32,
1689    //     staff: bool,
1690    // }
1691
1692    // #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
1693    // #[settings_key(key = "user")]
1694    // struct UserSettingsContent {
1695    //     name: Option<String>,
1696    //     age: Option<u32>,
1697    //     staff: Option<bool>,
1698    // }
1699
1700    // impl Settings for UserSettings {
1701    //     type FileContent = UserSettingsContent;
1702
1703    //     fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1704    //         sources.json_merge()
1705    //     }
1706
1707    //     fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
1708    //         vscode.u32_setting("user.age", &mut current.age);
1709    //     }
1710    // }
1711
1712    // #[derive(Debug, Deserialize, PartialEq)]
1713    // struct TurboSetting(bool);
1714
1715    // #[derive(
1716    //     Copy,
1717    //     Clone,
1718    //     PartialEq,
1719    //     Eq,
1720    //     Debug,
1721    //     Default,
1722    //     serde::Serialize,
1723    //     serde::Deserialize,
1724    //     SettingsUi,
1725    //     SettingsKey,
1726    //     JsonSchema,
1727    // )]
1728    // #[serde(default)]
1729    // #[settings_key(None)]
1730    // pub struct TurboSettingContent {
1731    //     turbo: Option<bool>,
1732    // }
1733
1734    // impl Settings for TurboSetting {
1735    //     type FileContent = TurboSettingContent;
1736
1737    //     fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1738    //         Ok(Self(
1739    //             sources
1740    //                 .user
1741    //                 .or(sources.server)
1742    //                 .unwrap_or(sources.default)
1743    //                 .turbo
1744    //                 .unwrap_or_default(),
1745    //         ))
1746    //     }
1747
1748    //     fn import_from_vscode(_vscode: &VsCodeSettings, _current: &mut Self::FileContent) {}
1749    // }
1750
1751    // #[derive(Clone, Debug, PartialEq, Deserialize)]
1752    // struct MultiKeySettings {
1753    //     #[serde(default)]
1754    //     key1: String,
1755    //     #[serde(default)]
1756    //     key2: String,
1757    // }
1758
1759    // #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
1760    // #[settings_key(None)]
1761    // struct MultiKeySettingsJson {
1762    //     key1: Option<String>,
1763    //     key2: Option<String>,
1764    // }
1765
1766    // impl Settings for MultiKeySettings {
1767    //     type FileContent = MultiKeySettingsJson;
1768
1769    //     fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1770    //         sources.json_merge()
1771    //     }
1772
1773    //     fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
1774    //         let first_value = vscode.read_string("key_1_first");
1775    //         let second_value = vscode.read_string("key_1_second");
1776
1777    //         if let Some((first, second)) = first_value.zip(second_value) {
1778    //             current.key1 = Some(format!("{} {}", first, second));
1779    //         }
1780    //     }
1781    // }
1782
1783    // #[derive(Debug, Deserialize)]
1784    // struct JournalSettings {
1785    //     #[expect(unused)]
1786    //     pub path: String,
1787    //     #[expect(unused)]
1788    //     pub hour_format: HourFormat,
1789    // }
1790
1791    // #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1792    // #[serde(rename_all = "snake_case")]
1793    // enum HourFormat {
1794    //     Hour12,
1795    //     Hour24,
1796    // }
1797
1798    // #[derive(
1799    //     Clone, Default, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey,
1800    // )]
1801    // #[settings_key(key = "journal")]
1802    // struct JournalSettingsJson {
1803    //     pub path: Option<String>,
1804    //     pub hour_format: Option<HourFormat>,
1805    // }
1806
1807    // impl Settings for JournalSettings {
1808    //     type FileContent = JournalSettingsJson;
1809
1810    //     fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1811    //         sources.json_merge()
1812    //     }
1813
1814    //     fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
1815    //         vscode.enum_setting("time_format", &mut current.hour_format, |s| match s {
1816    //             "12" => Some(HourFormat::Hour12),
1817    //             "24" => Some(HourFormat::Hour24),
1818    //             _ => None,
1819    //         });
1820    //     }
1821    // }
1822
1823    // #[gpui::test]
1824    // fn test_global_settings(cx: &mut App) {
1825    //     let mut store = SettingsStore::new(cx);
1826    //     store.register_setting::<UserSettings>(cx);
1827    //     store
1828    //         .set_default_settings(
1829    //             r#"{
1830    //                 "user": {
1831    //                     "name": "John Doe",
1832    //                     "age": 30,
1833    //                     "staff": false
1834    //                 }
1835    //             }"#,
1836    //             cx,
1837    //         )
1838    //         .unwrap();
1839
1840    //     // Set global settings - these should override defaults but not user settings
1841    //     store
1842    //         .set_global_settings(
1843    //             r#"{
1844    //                 "user": {
1845    //                     "name": "Global User",
1846    //                     "age": 35,
1847    //                     "staff": true
1848    //                 }
1849    //             }"#,
1850    //             cx,
1851    //         )
1852    //         .unwrap();
1853
1854    //     // Before user settings, global settings should apply
1855    //     assert_eq!(
1856    //         store.get::<UserSettings>(None),
1857    //         &UserSettings {
1858    //             name: "Global User".to_string(),
1859    //             age: 35,
1860    //             staff: true,
1861    //         }
1862    //     );
1863
1864    //     // Set user settings - these should override both defaults and global
1865    //     store
1866    //         .set_user_settings(
1867    //             r#"{
1868    //                 "user": {
1869    //                     "age": 40
1870    //                 }
1871    //             }"#,
1872    //             cx,
1873    //         )
1874    //         .unwrap();
1875
1876    //     // User settings should override global settings
1877    //     assert_eq!(
1878    //         store.get::<UserSettings>(None),
1879    //         &UserSettings {
1880    //             name: "Global User".to_string(), // Name from global settings
1881    //             age: 40,                         // Age from user settings
1882    //             staff: true,                     // Staff from global settings
1883    //         }
1884    //     );
1885    // }
1886
1887    // #[derive(
1888    //     Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey,
1889    // )]
1890    // #[settings_key(None)]
1891    // struct LanguageSettings {
1892    //     #[serde(default)]
1893    //     languages: HashMap<String, LanguageSettingEntry>,
1894    // }
1895
1896    // #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1897    // struct LanguageSettingEntry {
1898    //     language_setting_1: Option<bool>,
1899    //     language_setting_2: Option<bool>,
1900    // }
1901
1902    // impl Settings for LanguageSettings {
1903    //     type FileContent = Self;
1904
1905    //     fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1906    //         sources.json_merge()
1907    //     }
1908
1909    //     fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) {
1910    //         current.languages.extend(
1911    //             vscode
1912    //                 .read_value("vscode_languages")
1913    //                 .and_then(|value| value.as_array())
1914    //                 .map(|languages| {
1915    //                     languages
1916    //                         .iter()
1917    //                         .filter_map(|value| value.as_object())
1918    //                         .filter_map(|item| {
1919    //                             let mut rest = item.clone();
1920    //                             let name = rest.remove("name")?.as_str()?.to_string();
1921    //                             let entry = serde_json::from_value::<LanguageSettingEntry>(
1922    //                                 serde_json::Value::Object(rest),
1923    //                             )
1924    //                             .ok()?;
1925
1926    //                             Some((name, entry))
1927    //                         })
1928    //                 })
1929    //                 .into_iter()
1930    //                 .flatten(),
1931    //         );
1932    //     }
1933    // }
1934}