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