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_file(&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_file 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.as_ref().unwrap().content.clone();
 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.from_file(&self.default_settings, cx).unwrap();
 868                setting_value.refine(value.as_mut(), &refinements, cx);
 869                setting_value.set_global_value(value);
 870            }
 871
 872            // Reload the local values for the setting.
 873            paths_stack.clear();
 874            project_settings_stack.clear();
 875            for ((root_id, directory_path), local_settings) in &self.local_settings {
 876                // Build a stack of all of the local values for that setting.
 877                while let Some(prev_entry) = paths_stack.last() {
 878                    if let Some((prev_root_id, prev_path)) = prev_entry
 879                        && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
 880                    {
 881                        paths_stack.pop();
 882                        project_settings_stack.pop();
 883                        continue;
 884                    }
 885                    break;
 886                }
 887
 888                paths_stack.push(Some((*root_id, directory_path.as_ref())));
 889                project_settings_stack.push(local_settings);
 890
 891                // If a local settings file changed, then avoid recomputing local
 892                // settings for any path outside of that directory.
 893                if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
 894                    *root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
 895                }) {
 896                    continue;
 897                }
 898
 899                let mut value = setting_value.from_file(&self.default_settings, cx).unwrap();
 900                setting_value.refine(value.as_mut(), &refinements, cx);
 901                setting_value.refine(value.as_mut(), &project_settings_stack, cx);
 902                setting_value.set_local_value(*root_id, directory_path.clone(), value);
 903            }
 904        }
 905        Ok(())
 906    }
 907
 908    pub fn editorconfig_properties(
 909        &self,
 910        for_worktree: WorktreeId,
 911        for_path: &Path,
 912    ) -> Option<EditorconfigProperties> {
 913        let mut properties = EditorconfigProperties::new();
 914
 915        for (directory_with_config, _, parsed_editorconfig) in
 916            self.local_editorconfig_settings(for_worktree)
 917        {
 918            if !for_path.starts_with(&directory_with_config) {
 919                properties.use_fallbacks();
 920                return Some(properties);
 921            }
 922            let parsed_editorconfig = parsed_editorconfig?;
 923            if parsed_editorconfig.is_root {
 924                properties = EditorconfigProperties::new();
 925            }
 926            for section in parsed_editorconfig.sections {
 927                section.apply_to(&mut properties, for_path).log_err()?;
 928            }
 929        }
 930
 931        properties.use_fallbacks();
 932        Some(properties)
 933    }
 934}
 935
 936#[derive(Debug, Clone, PartialEq)]
 937pub enum InvalidSettingsError {
 938    LocalSettings { path: PathBuf, message: String },
 939    UserSettings { message: String },
 940    ServerSettings { message: String },
 941    DefaultSettings { message: String },
 942    Editorconfig { path: PathBuf, message: String },
 943    Tasks { path: PathBuf, message: String },
 944    Debug { path: PathBuf, message: String },
 945}
 946
 947impl std::fmt::Display for InvalidSettingsError {
 948    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 949        match self {
 950            InvalidSettingsError::LocalSettings { message, .. }
 951            | InvalidSettingsError::UserSettings { message }
 952            | InvalidSettingsError::ServerSettings { message }
 953            | InvalidSettingsError::DefaultSettings { message }
 954            | InvalidSettingsError::Tasks { message, .. }
 955            | InvalidSettingsError::Editorconfig { message, .. }
 956            | InvalidSettingsError::Debug { message, .. } => {
 957                write!(f, "{message}")
 958            }
 959        }
 960    }
 961}
 962impl std::error::Error for InvalidSettingsError {}
 963
 964impl Debug for SettingsStore {
 965    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 966        f.debug_struct("SettingsStore")
 967            .field(
 968                "types",
 969                &self
 970                    .setting_values
 971                    .values()
 972                    .map(|value| value.setting_type_name())
 973                    .collect::<Vec<_>>(),
 974            )
 975            .field("default_settings", &self.default_settings)
 976            .field("user_settings", &self.user_settings)
 977            .field("local_settings", &self.local_settings)
 978            .finish_non_exhaustive()
 979    }
 980}
 981
 982impl<T: Settings> AnySettingValue for SettingValue<T> {
 983    fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option<Box<dyn Any>> {
 984        T::from_default(s, cx).map(|result| Box::new(result) as _)
 985    }
 986
 987    fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent], cx: &mut App) {
 988        let value = value.downcast_mut::<T>().unwrap();
 989        for refinement in refinements {
 990            value.refine(refinement, cx)
 991        }
 992    }
 993
 994    fn setting_type_name(&self) -> &'static str {
 995        type_name::<T>()
 996    }
 997
 998    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)> {
 999        self.local_values
1000            .iter()
1001            .map(|(id, path, value)| (*id, path.clone(), value as _))
1002            .collect()
1003    }
1004
1005    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1006        if let Some(SettingsLocation { worktree_id, path }) = path {
1007            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1008                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1009                    return value;
1010                }
1011            }
1012        }
1013
1014        self.global_value
1015            .as_ref()
1016            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1017    }
1018
1019    fn set_global_value(&mut self, value: Box<dyn Any>) {
1020        self.global_value = Some(*value.downcast().unwrap());
1021    }
1022
1023    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>) {
1024        let value = *value.downcast().unwrap();
1025        match self
1026            .local_values
1027            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1028        {
1029            Ok(ix) => self.local_values[ix].2 = value,
1030            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1031        }
1032    }
1033
1034    fn import_from_vscode(
1035        &self,
1036        vscode_settings: &VsCodeSettings,
1037        settings_content: &mut SettingsContent,
1038    ) {
1039        T::import_from_vscode(vscode_settings, settings_content);
1040    }
1041}
1042
1043#[cfg(test)]
1044mod tests {
1045    use std::num::NonZeroU32;
1046
1047    use crate::{
1048        TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, default_settings,
1049        settings_content::LanguageSettingsContent, test_settings,
1050    };
1051
1052    use super::*;
1053    use unindent::Unindent;
1054    use util::MergeFrom;
1055
1056    #[derive(Debug, PartialEq)]
1057    struct AutoUpdateSetting {
1058        auto_update: bool,
1059    }
1060
1061    impl Settings for AutoUpdateSetting {
1062        fn from_default(content: &SettingsContent, _: &mut App) -> Option<Self> {
1063            content
1064                .auto_update
1065                .map(|auto_update| AutoUpdateSetting { auto_update })
1066        }
1067
1068        fn refine(&mut self, content: &SettingsContent, _: &mut App) {
1069            if let Some(auto_update) = content.auto_update {
1070                self.auto_update = auto_update;
1071            }
1072        }
1073
1074        fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {}
1075    }
1076
1077    #[derive(Debug, PartialEq)]
1078    struct TitleBarSettings {
1079        show: TitleBarVisibilityContent,
1080        show_branch_name: bool,
1081    }
1082
1083    impl Settings for TitleBarSettings {
1084        fn from_default(content: &SettingsContent, _: &mut App) -> Option<Self> {
1085            let content = content.title_bar.clone()?;
1086            Some(TitleBarSettings {
1087                show: content.show?,
1088                show_branch_name: content.show_branch_name?,
1089            })
1090        }
1091
1092        fn refine(&mut self, content: &SettingsContent, _: &mut App) {
1093            let Some(content) = content.title_bar.as_ref() else {
1094                return;
1095            };
1096            self.show.merge_from(&content.show)
1097        }
1098
1099        fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
1100            let mut show = None;
1101
1102            vscode.enum_setting("window.titleBarStyle", &mut show, |value| match value {
1103                "never" => Some(TitleBarVisibilityContent::Never),
1104                "always" => Some(TitleBarVisibilityContent::Always),
1105                _ => None,
1106            });
1107            if let Some(show) = show {
1108                content.title_bar.get_or_insert_default().show.replace(show);
1109            }
1110        }
1111    }
1112
1113    #[derive(Debug, PartialEq)]
1114    struct DefaultLanguageSettings {
1115        tab_size: NonZeroU32,
1116        preferred_line_length: u32,
1117    }
1118
1119    impl Settings for DefaultLanguageSettings {
1120        fn from_default(content: &SettingsContent, _: &mut App) -> Option<Self> {
1121            let content = &content.project.all_languages.defaults;
1122            Some(DefaultLanguageSettings {
1123                tab_size: content.tab_size?,
1124                preferred_line_length: content.preferred_line_length?,
1125            })
1126        }
1127
1128        fn refine(&mut self, content: &SettingsContent, _: &mut App) {
1129            let content = &content.project.all_languages.defaults;
1130            self.tab_size.merge_from(&content.tab_size);
1131            self.preferred_line_length
1132                .merge_from(&content.preferred_line_length);
1133        }
1134
1135        fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
1136            let content = &mut content.project.all_languages.defaults;
1137
1138            if let Some(size) = vscode
1139                .read_value("editor.tabSize")
1140                .and_then(|v| v.as_u64())
1141                .and_then(|n| NonZeroU32::new(n as u32))
1142            {
1143                content.tab_size = Some(size);
1144            }
1145        }
1146    }
1147
1148    #[gpui::test]
1149    fn test_settings_store_basic(cx: &mut App) {
1150        let mut store = SettingsStore::new(cx, &default_settings());
1151        store.register_setting::<AutoUpdateSetting>(cx);
1152        store.register_setting::<TitleBarSettings>(cx);
1153        store.register_setting::<DefaultLanguageSettings>(cx);
1154
1155        assert_eq!(
1156            store.get::<AutoUpdateSetting>(None),
1157            &AutoUpdateSetting { auto_update: true }
1158        );
1159        assert_eq!(
1160            store.get::<TitleBarSettings>(None).show,
1161            TitleBarVisibilityContent::Always
1162        );
1163
1164        store
1165            .set_user_settings(
1166                r#"{
1167                    "auto_update": false,
1168                    "title_bar": {
1169                      "show": "never"
1170                    }
1171                }"#,
1172                cx,
1173            )
1174            .unwrap();
1175
1176        assert_eq!(
1177            store.get::<AutoUpdateSetting>(None),
1178            &AutoUpdateSetting { auto_update: false }
1179        );
1180        assert_eq!(
1181            store.get::<TitleBarSettings>(None).show,
1182            TitleBarVisibilityContent::Never
1183        );
1184
1185        // todo!()
1186        store
1187            .set_local_settings(
1188                WorktreeId::from_usize(1),
1189                Path::new("/root1").into(),
1190                LocalSettingsKind::Settings,
1191                Some(r#"{ "tab_size": 5 }"#),
1192                cx,
1193            )
1194            .unwrap();
1195        store
1196            .set_local_settings(
1197                WorktreeId::from_usize(1),
1198                Path::new("/root1/subdir").into(),
1199                LocalSettingsKind::Settings,
1200                Some(r#"{ "preferred_line_length": 50 }"#),
1201                cx,
1202            )
1203            .unwrap();
1204
1205        store
1206            .set_local_settings(
1207                WorktreeId::from_usize(1),
1208                Path::new("/root2").into(),
1209                LocalSettingsKind::Settings,
1210                Some(r#"{ "tab_size": 9, "title_bar": { "show_branch_name": false } }"#),
1211                cx,
1212            )
1213            .unwrap();
1214
1215        assert_eq!(
1216            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1217                worktree_id: WorktreeId::from_usize(1),
1218                path: Path::new("/root1/something"),
1219            })),
1220            &DefaultLanguageSettings {
1221                preferred_line_length: 80,
1222                tab_size: 5.try_into().unwrap(),
1223            }
1224        );
1225        assert_eq!(
1226            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1227                worktree_id: WorktreeId::from_usize(1),
1228                path: Path::new("/root1/subdir/something")
1229            })),
1230            &DefaultLanguageSettings {
1231                preferred_line_length: 50,
1232                tab_size: 5.try_into().unwrap(),
1233            }
1234        );
1235        assert_eq!(
1236            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1237                worktree_id: WorktreeId::from_usize(1),
1238                path: Path::new("/root2/something")
1239            })),
1240            &DefaultLanguageSettings {
1241                preferred_line_length: 80,
1242                tab_size: 9.try_into().unwrap(),
1243            }
1244        );
1245        assert_eq!(
1246            store.get::<TitleBarSettings>(Some(SettingsLocation {
1247                worktree_id: WorktreeId::from_usize(1),
1248                path: Path::new("/root2/something")
1249            })),
1250            &TitleBarSettings {
1251                show: TitleBarVisibilityContent::Never,
1252                show_branch_name: true,
1253            }
1254        );
1255    }
1256
1257    #[gpui::test]
1258    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1259        let mut store = SettingsStore::new(cx, &test_settings());
1260        store
1261            .set_user_settings(r#"{ "auto_update": false }"#, cx)
1262            .unwrap();
1263        store.register_setting::<AutoUpdateSetting>(cx);
1264        store.register_setting::<TitleBarSettings>(cx);
1265
1266        assert_eq!(
1267            store.get::<AutoUpdateSetting>(None),
1268            &AutoUpdateSetting { auto_update: false }
1269        );
1270        assert_eq!(
1271            store.get::<TitleBarSettings>(None).show,
1272            TitleBarVisibilityContent::Always,
1273        );
1274    }
1275
1276    #[track_caller]
1277    fn check_settings_update(
1278        store: &mut SettingsStore,
1279        old_json: String,
1280        update: fn(&mut SettingsContent),
1281        expected_new_json: String,
1282        cx: &mut App,
1283    ) {
1284        store.set_user_settings(&old_json, cx).ok();
1285        let edits = store.edits_for_update(&old_json, update);
1286        dbg!(&edits);
1287        let mut new_json = old_json;
1288        for (range, replacement) in edits.into_iter() {
1289            new_json.replace_range(range, &replacement);
1290        }
1291        pretty_assertions::assert_eq!(new_json, expected_new_json);
1292    }
1293
1294    #[gpui::test]
1295    fn test_setting_store_update(cx: &mut App) {
1296        let mut store = SettingsStore::new(cx, &test_settings());
1297
1298        // entries added and updated
1299        check_settings_update(
1300            &mut store,
1301            r#"{
1302                "languages": {
1303                    "JSON": {
1304                        "auto_indent": true
1305                    }
1306                }
1307            }"#
1308            .unindent(),
1309            |settings| {
1310                settings
1311                    .languages_mut()
1312                    .get_mut("JSON")
1313                    .unwrap()
1314                    .auto_indent = Some(false);
1315
1316                settings.languages_mut().insert(
1317                    "Rust".into(),
1318                    LanguageSettingsContent {
1319                        auto_indent: Some(true),
1320                        ..Default::default()
1321                    },
1322                );
1323            },
1324            r#"{
1325                "languages": {
1326                    "Rust": {
1327                        "auto_indent": true
1328                    },
1329                    "JSON": {
1330                        "auto_indent": false
1331                    }
1332                }
1333            }"#
1334            .unindent(),
1335            cx,
1336        );
1337
1338        // entries removed
1339        check_settings_update(
1340            &mut store,
1341            r#"{
1342                "languages": {
1343                    "Rust": {
1344                        "language_setting_2": true
1345                    },
1346                    "JSON": {
1347                        "language_setting_1": false
1348                    }
1349                }
1350            }"#
1351            .unindent(),
1352            |settings| {
1353                settings.languages_mut().remove("JSON").unwrap();
1354            },
1355            r#"{
1356                "languages": {
1357                    "Rust": {
1358                        "language_setting_2": true
1359                    }
1360                }
1361            }"#
1362            .unindent(),
1363            cx,
1364        );
1365
1366        check_settings_update(
1367            &mut store,
1368            r#"{
1369                "languages": {
1370                    "Rust": {
1371                        "language_setting_2": true
1372                    },
1373                    "JSON": {
1374                        "language_setting_1": false
1375                    }
1376                }
1377            }"#
1378            .unindent(),
1379            |settings| {
1380                settings.languages_mut().remove("Rust").unwrap();
1381            },
1382            r#"{
1383                "languages": {
1384                    "JSON": {
1385                        "language_setting_1": false
1386                    }
1387                }
1388            }"#
1389            .unindent(),
1390            cx,
1391        );
1392
1393        // weird formatting
1394        check_settings_update(
1395            &mut store,
1396            r#"{
1397                "title_bar":   { "show": "always", "name": "Max"  }
1398                }"#
1399            .unindent(),
1400            |settings| {
1401                dbg!(&settings.title_bar);
1402                settings.title_bar.as_mut().unwrap().show = Some(TitleBarVisibilityContent::Never);
1403                dbg!(&settings.title_bar);
1404            },
1405            r#"{
1406                "title_bar":   { "show": "never", "name": "Max"  }
1407                }"#
1408            .unindent(),
1409            cx,
1410        );
1411
1412        // single-line formatting, other keys
1413        check_settings_update(
1414            &mut store,
1415            r#"{ "one": 1, "two": 2 }"#.to_owned(),
1416            |settings| settings.auto_update = Some(true),
1417            r#"{ "auto_update": true, "one": 1, "two": 2 }"#.to_owned(),
1418            cx,
1419        );
1420
1421        // empty object
1422        check_settings_update(
1423            &mut store,
1424            r#"{
1425                "title_bar": {}
1426            }"#
1427            .unindent(),
1428            |settings| settings.title_bar.as_mut().unwrap().show_menus = Some(true),
1429            r#"{
1430                "title_bar": {
1431                    "show_menus": true
1432                }
1433            }"#
1434            .unindent(),
1435            cx,
1436        );
1437
1438        // no content
1439        check_settings_update(
1440            &mut store,
1441            r#""#.unindent(),
1442            |settings| {
1443                settings.title_bar = Some(TitleBarSettingsContent {
1444                    show_branch_name: Some(true),
1445                    ..Default::default()
1446                })
1447            },
1448            r#"{
1449                "title_bar": {
1450                    "show_branch_name": true
1451                }
1452            }
1453            "#
1454            .unindent(),
1455            cx,
1456        );
1457
1458        check_settings_update(
1459            &mut store,
1460            r#"{
1461            }
1462            "#
1463            .unindent(),
1464            |settings| settings.title_bar.get_or_insert_default().show_branch_name = Some(true),
1465            r#"{
1466                "title_bar": {
1467                    "show_branch_name": true
1468                }
1469            }
1470            "#
1471            .unindent(),
1472            cx,
1473        );
1474    }
1475
1476    #[gpui::test]
1477    fn test_vscode_import(cx: &mut App) {
1478        let mut store = SettingsStore::new(cx, &test_settings());
1479        store.register_setting::<DefaultLanguageSettings>(cx);
1480        store.register_setting::<TitleBarSettings>(cx);
1481        store.register_setting::<AutoUpdateSetting>(cx);
1482
1483        // create settings that werent present
1484        check_vscode_import(
1485            &mut store,
1486            r#"{
1487            }
1488            "#
1489            .unindent(),
1490            r#" { "editor.tabSize": 37 } "#.to_owned(),
1491            r#"{
1492                "tab_size": 37
1493            }
1494            "#
1495            .unindent(),
1496            cx,
1497        );
1498
1499        // persist settings that were present
1500        check_vscode_import(
1501            &mut store,
1502            r#"{
1503                "preferred_line_length": 99,
1504            }
1505            "#
1506            .unindent(),
1507            r#"{ "editor.tabSize": 42 }"#.to_owned(),
1508            r#"{
1509                "tab_size": 42,
1510                "preferred_line_length": 99,
1511            }
1512            "#
1513            .unindent(),
1514            cx,
1515        );
1516
1517        // don't clobber settings that aren't present in vscode
1518        check_vscode_import(
1519            &mut store,
1520            r#"{
1521                "preferred_line_length": 99,
1522                "tab_size": 42
1523            }
1524            "#
1525            .unindent(),
1526            r#"{}"#.to_owned(),
1527            r#"{
1528                "preferred_line_length": 99,
1529                "tab_size": 42
1530            }
1531            "#
1532            .unindent(),
1533            cx,
1534        );
1535
1536        // custom enum
1537        check_vscode_import(
1538            &mut store,
1539            r#"{
1540                "title_bar": {
1541                "show": "always"
1542                }
1543            }
1544            "#
1545            .unindent(),
1546            r#"{ "window.titleBarStyle": "never" }"#.to_owned(),
1547            r#"{
1548                "title_bar": {
1549                "show": "never"
1550                }
1551            }
1552            "#
1553            .unindent(),
1554            cx,
1555        );
1556    }
1557
1558    #[track_caller]
1559    fn check_vscode_import(
1560        store: &mut SettingsStore,
1561        old: String,
1562        vscode: String,
1563        expected: String,
1564        cx: &mut App,
1565    ) {
1566        store.set_user_settings(&old, cx).ok();
1567        let new = store.get_vscode_edits(
1568            old,
1569            &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
1570        );
1571        pretty_assertions::assert_eq!(new, expected);
1572    }
1573
1574    #[gpui::test]
1575    fn test_global_settings(cx: &mut App) {
1576        let mut store = SettingsStore::new(cx, &test_settings());
1577        store.register_setting::<TitleBarSettings>(cx);
1578
1579        // Set global settings - these should override defaults but not user settings
1580        store
1581            .set_global_settings(
1582                r#"{
1583                    "title_bar": {
1584                        "show": "never",
1585                    }
1586                }"#,
1587                cx,
1588            )
1589            .unwrap();
1590
1591        // Before user settings, global settings should apply
1592        assert_eq!(
1593            store.get::<TitleBarSettings>(None),
1594            &TitleBarSettings {
1595                show: TitleBarVisibilityContent::Never,
1596                show_branch_name: true,
1597            }
1598        );
1599
1600        // Set user settings - these should override both defaults and global
1601        store
1602            .set_user_settings(
1603                r#"{
1604                    "title_bar": {
1605                        "show": "always"
1606                    }
1607                }"#,
1608                cx,
1609            )
1610            .unwrap();
1611
1612        // User settings should override global settings
1613        assert_eq!(
1614            store.get::<TitleBarSettings>(None),
1615            &TitleBarSettings {
1616                show: TitleBarVisibilityContent::Always,
1617                show_branch_name: true, // Staff from global settings
1618            }
1619        );
1620    }
1621}