settings_store.rs

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