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