settings_store.rs

   1use anyhow::{Context as _, Result};
   2use collections::{BTreeMap, HashMap, btree_map, hash_map};
   3use fs::Fs;
   4use futures::{
   5    FutureExt, StreamExt,
   6    channel::{mpsc, oneshot},
   7    future::LocalBoxFuture,
   8};
   9use gpui::{
  10    App, AppContext, AsyncApp, BorrowAppContext, Entity, Global, SharedString, Task, UpdateGlobal,
  11};
  12
  13use paths::{local_settings_file_relative_path, task_file_name};
  14use schemars::{JsonSchema, json_schema};
  15use serde_json::Value;
  16use settings_content::ParseStatus;
  17use std::{
  18    any::{Any, TypeId, type_name},
  19    fmt::Debug,
  20    ops::Range,
  21    path::{Path, PathBuf},
  22    rc::Rc,
  23    str,
  24    sync::Arc,
  25};
  26use util::{
  27    ResultExt as _,
  28    rel_path::RelPath,
  29    schemars::{AllowTrailingCommas, DefaultDenyUnknownFields, replace_subschema},
  30};
  31
  32use crate::editorconfig_store::EditorconfigStore;
  33
  34use crate::{
  35    ActiveSettingsProfileName, FontFamilyName, IconThemeName, LanguageSettingsContent,
  36    LanguageToSettingsMap, LspSettings, LspSettingsMap, ThemeName, UserSettingsContentExt,
  37    VsCodeSettings, WorktreeId,
  38    settings_content::{
  39        ExtensionsSettingsContent, ProjectSettingsContent, RootUserSettings, SettingsContent,
  40        UserSettingsContent, merge_from::MergeFrom,
  41    },
  42};
  43
  44use settings_json::{infer_json_indent_size, update_value_in_json_text};
  45
  46pub const LSP_SETTINGS_SCHEMA_URL_PREFIX: &str = "zed://schemas/settings/lsp/";
  47
  48pub trait SettingsKey: 'static + Send + Sync {
  49    /// The name of a key within the JSON file from which this setting should
  50    /// be deserialized. If this is `None`, then the setting will be deserialized
  51    /// from the root object.
  52    const KEY: Option<&'static str>;
  53
  54    const FALLBACK_KEY: Option<&'static str> = None;
  55}
  56
  57/// A value that can be defined as a user setting.
  58///
  59/// Settings can be loaded from a combination of multiple JSON files.
  60pub trait Settings: 'static + Send + Sync + Sized {
  61    /// The name of the keys in the [`FileContent`](Self::FileContent) that should
  62    /// always be written to a settings file, even if their value matches the default
  63    /// value.
  64    ///
  65    /// This is useful for tagged [`FileContent`](Self::FileContent)s where the tag
  66    /// is a "version" field that should always be persisted, even if the current
  67    /// user settings match the current version of the settings.
  68    const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
  69
  70    /// Read the value from default.json.
  71    ///
  72    /// This function *should* panic if default values are missing,
  73    /// and you should add a default to default.json for documentation.
  74    fn from_settings(content: &SettingsContent) -> Self;
  75
  76    #[track_caller]
  77    fn register(cx: &mut App)
  78    where
  79        Self: Sized,
  80    {
  81        SettingsStore::update_global(cx, |store, _| {
  82            store.register_setting::<Self>();
  83        });
  84    }
  85
  86    #[track_caller]
  87    fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self
  88    where
  89        Self: Sized,
  90    {
  91        cx.global::<SettingsStore>().get(path)
  92    }
  93
  94    #[track_caller]
  95    fn get_global(cx: &App) -> &Self
  96    where
  97        Self: Sized,
  98    {
  99        cx.global::<SettingsStore>().get(None)
 100    }
 101
 102    #[track_caller]
 103    fn try_get(cx: &App) -> Option<&Self>
 104    where
 105        Self: Sized,
 106    {
 107        if cx.has_global::<SettingsStore>() {
 108            cx.global::<SettingsStore>().try_get(None)
 109        } else {
 110            None
 111        }
 112    }
 113
 114    #[track_caller]
 115    fn try_read_global<R>(cx: &AsyncApp, f: impl FnOnce(&Self) -> R) -> Option<R>
 116    where
 117        Self: Sized,
 118    {
 119        cx.try_read_global(|s: &SettingsStore, _| f(s.get(None)))
 120    }
 121
 122    #[track_caller]
 123    fn override_global(settings: Self, cx: &mut App)
 124    where
 125        Self: Sized,
 126    {
 127        cx.global_mut::<SettingsStore>().override_global(settings)
 128    }
 129}
 130
 131pub struct RegisteredSetting {
 132    pub settings_value: fn() -> Box<dyn AnySettingValue>,
 133    pub from_settings: fn(&SettingsContent) -> Box<dyn Any>,
 134    pub id: fn() -> TypeId,
 135}
 136
 137inventory::collect!(RegisteredSetting);
 138
 139#[derive(Clone, Copy, Debug)]
 140pub struct SettingsLocation<'a> {
 141    pub worktree_id: WorktreeId,
 142    pub path: &'a RelPath,
 143}
 144
 145pub struct SettingsStore {
 146    setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
 147    default_settings: Rc<SettingsContent>,
 148    user_settings: Option<UserSettingsContent>,
 149    global_settings: Option<Box<SettingsContent>>,
 150
 151    extension_settings: Option<Box<SettingsContent>>,
 152    server_settings: Option<Box<SettingsContent>>,
 153
 154    merged_settings: Rc<SettingsContent>,
 155
 156    local_settings: BTreeMap<(WorktreeId, Arc<RelPath>), SettingsContent>,
 157    pub editorconfig_store: Entity<EditorconfigStore>,
 158
 159    _setting_file_updates: Task<()>,
 160    setting_file_updates_tx:
 161        mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,
 162    file_errors: BTreeMap<SettingsFile, SettingsParseResult>,
 163}
 164
 165#[derive(Clone, PartialEq, Eq, Debug)]
 166pub enum SettingsFile {
 167    Default,
 168    Global,
 169    User,
 170    Server,
 171    /// Represents project settings in ssh projects as well as local projects
 172    Project((WorktreeId, Arc<RelPath>)),
 173}
 174
 175impl PartialOrd for SettingsFile {
 176    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 177        Some(self.cmp(other))
 178    }
 179}
 180
 181/// Sorted in order of precedence
 182impl Ord for SettingsFile {
 183    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 184        use SettingsFile::*;
 185        use std::cmp::Ordering;
 186        match (self, other) {
 187            (User, User) => Ordering::Equal,
 188            (Server, Server) => Ordering::Equal,
 189            (Default, Default) => Ordering::Equal,
 190            (Project((id1, rel_path1)), Project((id2, rel_path2))) => id1
 191                .cmp(id2)
 192                .then_with(|| rel_path1.cmp(rel_path2).reverse()),
 193            (Project(_), _) => Ordering::Less,
 194            (_, Project(_)) => Ordering::Greater,
 195            (Server, _) => Ordering::Less,
 196            (_, Server) => Ordering::Greater,
 197            (User, _) => Ordering::Less,
 198            (_, User) => Ordering::Greater,
 199            (Global, _) => Ordering::Less,
 200            (_, Global) => Ordering::Greater,
 201        }
 202    }
 203}
 204
 205#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 206pub enum LocalSettingsKind {
 207    Settings,
 208    Tasks,
 209    Editorconfig,
 210    Debug,
 211}
 212
 213#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 214pub enum LocalSettingsPath {
 215    InWorktree(Arc<RelPath>),
 216    OutsideWorktree(Arc<Path>),
 217}
 218
 219impl LocalSettingsPath {
 220    pub fn is_outside_worktree(&self) -> bool {
 221        matches!(self, Self::OutsideWorktree(_))
 222    }
 223
 224    pub fn to_proto(&self) -> String {
 225        match self {
 226            Self::InWorktree(path) => path.to_proto(),
 227            Self::OutsideWorktree(path) => path.to_string_lossy().to_string(),
 228        }
 229    }
 230
 231    pub fn from_proto(path: &str, is_outside_worktree: bool) -> anyhow::Result<Self> {
 232        if is_outside_worktree {
 233            Ok(Self::OutsideWorktree(PathBuf::from(path).into()))
 234        } else {
 235            Ok(Self::InWorktree(RelPath::from_proto(path)?))
 236        }
 237    }
 238}
 239
 240impl Global for SettingsStore {}
 241
 242#[doc(hidden)]
 243#[derive(Debug)]
 244pub struct SettingValue<T> {
 245    #[doc(hidden)]
 246    pub global_value: Option<T>,
 247    #[doc(hidden)]
 248    pub local_values: Vec<(WorktreeId, Arc<RelPath>, T)>,
 249}
 250
 251#[doc(hidden)]
 252pub trait AnySettingValue: 'static + Send + Sync {
 253    fn setting_type_name(&self) -> &'static str;
 254
 255    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any>;
 256
 257    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
 258    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)>;
 259    fn set_global_value(&mut self, value: Box<dyn Any>);
 260    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<RelPath>, value: Box<dyn Any>);
 261    fn clear_local_values(&mut self, root_id: WorktreeId);
 262}
 263
 264/// Parameters that are used when generating some JSON schemas at runtime.
 265pub struct SettingsJsonSchemaParams<'a> {
 266    pub language_names: &'a [String],
 267    pub font_names: &'a [String],
 268    pub theme_names: &'a [SharedString],
 269    pub icon_theme_names: &'a [SharedString],
 270    pub lsp_adapter_names: &'a [String],
 271}
 272
 273impl SettingsStore {
 274    pub fn new(cx: &mut App, default_settings: &str) -> Self {
 275        let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
 276        let default_settings: Rc<SettingsContent> =
 277            SettingsContent::parse_json_with_comments(default_settings)
 278                .unwrap()
 279                .into();
 280        let mut this = Self {
 281            setting_values: Default::default(),
 282            default_settings: default_settings.clone(),
 283            global_settings: None,
 284            server_settings: None,
 285            user_settings: None,
 286            extension_settings: None,
 287
 288            merged_settings: default_settings,
 289            local_settings: BTreeMap::default(),
 290            editorconfig_store: cx.new(|_| EditorconfigStore::default()),
 291            setting_file_updates_tx,
 292            _setting_file_updates: cx.spawn(async move |cx| {
 293                while let Some(setting_file_update) = setting_file_updates_rx.next().await {
 294                    (setting_file_update)(cx.clone()).await.log_err();
 295                }
 296            }),
 297            file_errors: BTreeMap::default(),
 298        };
 299
 300        this.load_settings_types();
 301
 302        this
 303    }
 304
 305    pub fn observe_active_settings_profile_name(cx: &mut App) -> gpui::Subscription {
 306        cx.observe_global::<ActiveSettingsProfileName>(|cx| {
 307            Self::update_global(cx, |store, cx| {
 308                store.recompute_values(None, cx);
 309            });
 310        })
 311    }
 312
 313    pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R
 314    where
 315        C: BorrowAppContext,
 316    {
 317        cx.update_global(f)
 318    }
 319
 320    /// Add a new type of setting to the store.
 321    pub fn register_setting<T: Settings>(&mut self) {
 322        self.register_setting_internal(&RegisteredSetting {
 323            settings_value: || {
 324                Box::new(SettingValue::<T> {
 325                    global_value: None,
 326                    local_values: Vec::new(),
 327                })
 328            },
 329            from_settings: |content| Box::new(T::from_settings(content)),
 330            id: || TypeId::of::<T>(),
 331        });
 332    }
 333
 334    fn load_settings_types(&mut self) {
 335        for registered_setting in inventory::iter::<RegisteredSetting>() {
 336            self.register_setting_internal(registered_setting);
 337        }
 338    }
 339
 340    fn register_setting_internal(&mut self, registered_setting: &RegisteredSetting) {
 341        let entry = self.setting_values.entry((registered_setting.id)());
 342
 343        if matches!(entry, hash_map::Entry::Occupied(_)) {
 344            return;
 345        }
 346
 347        let setting_value = entry.or_insert((registered_setting.settings_value)());
 348        let value = (registered_setting.from_settings)(&self.merged_settings);
 349        setting_value.set_global_value(value);
 350    }
 351
 352    /// Get the value of a setting.
 353    ///
 354    /// Panics if the given setting type has not been registered, or if there is no
 355    /// value for this setting.
 356    pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T {
 357        self.setting_values
 358            .get(&TypeId::of::<T>())
 359            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 360            .value_for_path(path)
 361            .downcast_ref::<T>()
 362            .expect("no default value for setting type")
 363    }
 364
 365    /// Get the value of a setting.
 366    ///
 367    /// Does not panic
 368    pub fn try_get<T: Settings>(&self, path: Option<SettingsLocation>) -> Option<&T> {
 369        self.setting_values
 370            .get(&TypeId::of::<T>())
 371            .map(|value| value.value_for_path(path))
 372            .and_then(|value| value.downcast_ref::<T>())
 373    }
 374
 375    /// Get all values from project specific settings
 376    pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<RelPath>, &T)> {
 377        self.setting_values
 378            .get(&TypeId::of::<T>())
 379            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 380            .all_local_values()
 381            .into_iter()
 382            .map(|(id, path, any)| {
 383                (
 384                    id,
 385                    path,
 386                    any.downcast_ref::<T>()
 387                        .expect("wrong value type for setting"),
 388                )
 389            })
 390            .collect()
 391    }
 392
 393    /// Override the global value for a setting.
 394    ///
 395    /// The given value will be overwritten if the user settings file changes.
 396    pub fn override_global<T: Settings>(&mut self, value: T) {
 397        self.setting_values
 398            .get_mut(&TypeId::of::<T>())
 399            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
 400            .set_global_value(Box::new(value))
 401    }
 402
 403    /// Get the user's settings content.
 404    ///
 405    /// For user-facing functionality use the typed setting interface.
 406    /// (e.g. ProjectSettings::get_global(cx))
 407    pub fn raw_user_settings(&self) -> Option<&UserSettingsContent> {
 408        self.user_settings.as_ref()
 409    }
 410
 411    /// Get the default settings content as a raw JSON value.
 412    pub fn raw_default_settings(&self) -> &SettingsContent {
 413        &self.default_settings
 414    }
 415
 416    /// Get the configured settings profile names.
 417    pub fn configured_settings_profiles(&self) -> impl Iterator<Item = &str> {
 418        self.user_settings
 419            .iter()
 420            .flat_map(|settings| settings.profiles.keys().map(|k| k.as_str()))
 421    }
 422
 423    #[cfg(any(test, feature = "test-support"))]
 424    pub fn test(cx: &mut App) -> Self {
 425        Self::new(cx, &crate::test_settings())
 426    }
 427
 428    /// Updates the value of a setting in the user's global configuration.
 429    ///
 430    /// This is only for tests. Normally, settings are only loaded from
 431    /// JSON files.
 432    #[cfg(any(test, feature = "test-support"))]
 433    pub fn update_user_settings(
 434        &mut self,
 435        cx: &mut App,
 436        update: impl FnOnce(&mut SettingsContent),
 437    ) {
 438        let mut content = self.user_settings.clone().unwrap_or_default().content;
 439        update(&mut content);
 440        let new_text = serde_json::to_string(&UserSettingsContent {
 441            content,
 442            ..Default::default()
 443        })
 444        .unwrap();
 445        _ = self.set_user_settings(&new_text, cx);
 446    }
 447
 448    pub async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {
 449        match fs.load(paths::settings_file()).await {
 450            result @ Ok(_) => result,
 451            Err(err) => {
 452                if let Some(e) = err.downcast_ref::<std::io::Error>()
 453                    && e.kind() == std::io::ErrorKind::NotFound
 454                {
 455                    return Ok(crate::initial_user_settings_content().to_string());
 456                }
 457                Err(err)
 458            }
 459        }
 460    }
 461
 462    fn update_settings_file_inner(
 463        &self,
 464        fs: Arc<dyn Fs>,
 465        update: impl 'static + Send + FnOnce(String, AsyncApp) -> Result<String>,
 466    ) -> oneshot::Receiver<Result<()>> {
 467        let (tx, rx) = oneshot::channel::<Result<()>>();
 468        self.setting_file_updates_tx
 469            .unbounded_send(Box::new(move |cx: AsyncApp| {
 470                async move {
 471                    let res = async move {
 472                        let old_text = Self::load_settings(&fs).await?;
 473                        let new_text = update(old_text, cx)?;
 474                        let settings_path = paths::settings_file().as_path();
 475                        if fs.is_file(settings_path).await {
 476                            let resolved_path =
 477                                fs.canonicalize(settings_path).await.with_context(|| {
 478                                    format!(
 479                                        "Failed to canonicalize settings path {:?}",
 480                                        settings_path
 481                                    )
 482                                })?;
 483
 484                            fs.atomic_write(resolved_path.clone(), new_text)
 485                                .await
 486                                .with_context(|| {
 487                                    format!("Failed to write settings to file {:?}", resolved_path)
 488                                })?;
 489                        } else {
 490                            fs.atomic_write(settings_path.to_path_buf(), new_text)
 491                                .await
 492                                .with_context(|| {
 493                                    format!("Failed to write settings to file {:?}", settings_path)
 494                                })?;
 495                        }
 496                        anyhow::Ok(())
 497                    }
 498                    .await;
 499
 500                    let new_res = match &res {
 501                        Ok(_) => anyhow::Ok(()),
 502                        Err(e) => Err(anyhow::anyhow!("Failed to write settings to file {:?}", e)),
 503                    };
 504
 505                    _ = tx.send(new_res);
 506                    res
 507                }
 508                .boxed_local()
 509            }))
 510            .map_err(|err| anyhow::format_err!("Failed to update settings file: {}", err))
 511            .log_with_level(log::Level::Warn);
 512        return rx;
 513    }
 514
 515    pub fn update_settings_file(
 516        &self,
 517        fs: Arc<dyn Fs>,
 518        update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
 519    ) {
 520        _ = self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| {
 521            Ok(cx.read_global(|store: &SettingsStore, cx| {
 522                store.new_text_for_update(old_text, |content| update(content, cx))
 523            }))
 524        });
 525    }
 526
 527    pub fn import_vscode_settings(
 528        &self,
 529        fs: Arc<dyn Fs>,
 530        vscode_settings: VsCodeSettings,
 531    ) -> oneshot::Receiver<Result<()>> {
 532        self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| {
 533            Ok(cx.read_global(|store: &SettingsStore, _cx| {
 534                store.get_vscode_edits(old_text, &vscode_settings)
 535            }))
 536        })
 537    }
 538
 539    pub fn get_all_files(&self) -> Vec<SettingsFile> {
 540        let mut files = Vec::from_iter(
 541            self.local_settings
 542                .keys()
 543                // rev because these are sorted by path, so highest precedence is last
 544                .rev()
 545                .cloned()
 546                .map(SettingsFile::Project),
 547        );
 548
 549        if self.server_settings.is_some() {
 550            files.push(SettingsFile::Server);
 551        }
 552        // ignoring profiles
 553        // ignoring os profiles
 554        // ignoring release channel profiles
 555        // ignoring global
 556        // ignoring extension
 557
 558        if self.user_settings.is_some() {
 559            files.push(SettingsFile::User);
 560        }
 561        files.push(SettingsFile::Default);
 562        files
 563    }
 564
 565    pub fn get_content_for_file(&self, file: SettingsFile) -> Option<&SettingsContent> {
 566        match file {
 567            SettingsFile::User => self
 568                .user_settings
 569                .as_ref()
 570                .map(|settings| settings.content.as_ref()),
 571            SettingsFile::Default => Some(self.default_settings.as_ref()),
 572            SettingsFile::Server => self.server_settings.as_deref(),
 573            SettingsFile::Project(ref key) => self.local_settings.get(key),
 574            SettingsFile::Global => self.global_settings.as_deref(),
 575        }
 576    }
 577
 578    pub fn get_overrides_for_field<T>(
 579        &self,
 580        target_file: SettingsFile,
 581        get: fn(&SettingsContent) -> &Option<T>,
 582    ) -> Vec<SettingsFile> {
 583        let all_files = self.get_all_files();
 584        let mut found_file = false;
 585        let mut overrides = Vec::new();
 586
 587        for file in all_files.into_iter().rev() {
 588            if !found_file {
 589                found_file = file == target_file;
 590                continue;
 591            }
 592
 593            if let SettingsFile::Project((wt_id, ref path)) = file
 594                && let SettingsFile::Project((target_wt_id, ref target_path)) = target_file
 595                && (wt_id != target_wt_id || !target_path.starts_with(path))
 596            {
 597                // if requesting value from a local file, don't return values from local files in different worktrees
 598                continue;
 599            }
 600
 601            let Some(content) = self.get_content_for_file(file.clone()) else {
 602                continue;
 603            };
 604            if get(content).is_some() {
 605                overrides.push(file);
 606            }
 607        }
 608
 609        overrides
 610    }
 611
 612    /// Checks the given file, and files that the passed file overrides for the given field.
 613    /// Returns the first file found that contains the value.
 614    /// The value will only be None if no file contains the value.
 615    /// I.e. if no file contains the value, returns `(File::Default, None)`
 616    pub fn get_value_from_file<'a, T: 'a>(
 617        &'a self,
 618        target_file: SettingsFile,
 619        pick: fn(&'a SettingsContent) -> Option<T>,
 620    ) -> (SettingsFile, Option<T>) {
 621        self.get_value_from_file_inner(target_file, pick, true)
 622    }
 623
 624    /// Same as `Self::get_value_from_file` except that it does not include the current file.
 625    /// Therefore it returns the value that was potentially overloaded by the target file.
 626    pub fn get_value_up_to_file<'a, T: 'a>(
 627        &'a self,
 628        target_file: SettingsFile,
 629        pick: fn(&'a SettingsContent) -> Option<T>,
 630    ) -> (SettingsFile, Option<T>) {
 631        self.get_value_from_file_inner(target_file, pick, false)
 632    }
 633
 634    fn get_value_from_file_inner<'a, T: 'a>(
 635        &'a self,
 636        target_file: SettingsFile,
 637        pick: fn(&'a SettingsContent) -> Option<T>,
 638        include_target_file: bool,
 639    ) -> (SettingsFile, Option<T>) {
 640        // todo(settings_ui): Add a metadata field for overriding the "overrides" tag, for contextually different settings
 641        //  e.g. disable AI isn't overridden, or a vec that gets extended instead or some such
 642
 643        // todo(settings_ui) cache all files
 644        let all_files = self.get_all_files();
 645        let mut found_file = false;
 646
 647        for file in all_files.into_iter() {
 648            if !found_file && file != SettingsFile::Default {
 649                if file != target_file {
 650                    continue;
 651                }
 652                found_file = true;
 653                if !include_target_file {
 654                    continue;
 655                }
 656            }
 657
 658            if let SettingsFile::Project((worktree_id, ref path)) = file
 659                && let SettingsFile::Project((target_worktree_id, ref target_path)) = target_file
 660                && (worktree_id != target_worktree_id || !target_path.starts_with(&path))
 661            {
 662                // if requesting value from a local file, don't return values from local files in different worktrees
 663                continue;
 664            }
 665
 666            let Some(content) = self.get_content_for_file(file.clone()) else {
 667                continue;
 668            };
 669            if let Some(value) = pick(content) {
 670                return (file, Some(value));
 671            }
 672        }
 673
 674        (SettingsFile::Default, None)
 675    }
 676
 677    #[inline(always)]
 678    fn parse_and_migrate_zed_settings<SettingsContentType: RootUserSettings>(
 679        &mut self,
 680        user_settings_content: &str,
 681        file: SettingsFile,
 682    ) -> (Option<SettingsContentType>, SettingsParseResult) {
 683        let mut migration_status = MigrationStatus::NotNeeded;
 684        let (settings, parse_status) = if user_settings_content.is_empty() {
 685            SettingsContentType::parse_json("{}")
 686        } else {
 687            let migration_res = migrator::migrate_settings(user_settings_content);
 688            migration_status = match &migration_res {
 689                Ok(Some(_)) => MigrationStatus::Succeeded,
 690                Ok(None) => MigrationStatus::NotNeeded,
 691                Err(err) => MigrationStatus::Failed {
 692                    error: err.to_string(),
 693                },
 694            };
 695            let content = match &migration_res {
 696                Ok(Some(content)) => content,
 697                Ok(None) => user_settings_content,
 698                Err(_) => user_settings_content,
 699            };
 700            SettingsContentType::parse_json(content)
 701        };
 702
 703        let result = SettingsParseResult {
 704            parse_status,
 705            migration_status,
 706        };
 707        self.file_errors.insert(file, result.clone());
 708        return (settings, result);
 709    }
 710
 711    pub fn error_for_file(&self, file: SettingsFile) -> Option<SettingsParseResult> {
 712        self.file_errors
 713            .get(&file)
 714            .filter(|parse_result| parse_result.requires_user_action())
 715            .cloned()
 716    }
 717}
 718
 719impl SettingsStore {
 720    /// Updates the value of a setting in a JSON file, returning the new text
 721    /// for that JSON file.
 722    pub fn new_text_for_update(
 723        &self,
 724        old_text: String,
 725        update: impl FnOnce(&mut SettingsContent),
 726    ) -> String {
 727        let edits = self.edits_for_update(&old_text, update);
 728        let mut new_text = old_text;
 729        for (range, replacement) in edits.into_iter() {
 730            new_text.replace_range(range, &replacement);
 731        }
 732        new_text
 733    }
 734
 735    pub fn get_vscode_edits(&self, old_text: String, vscode: &VsCodeSettings) -> String {
 736        self.new_text_for_update(old_text, |content| {
 737            content.merge_from(&vscode.settings_content())
 738        })
 739    }
 740
 741    /// Updates the value of a setting in a JSON file, returning a list
 742    /// of edits to apply to the JSON file.
 743    pub fn edits_for_update(
 744        &self,
 745        text: &str,
 746        update: impl FnOnce(&mut SettingsContent),
 747    ) -> Vec<(Range<usize>, String)> {
 748        let old_content = UserSettingsContent::parse_json_with_comments(text)
 749            .log_err()
 750            .unwrap_or_default();
 751        let mut new_content = old_content.clone();
 752        update(&mut new_content.content);
 753
 754        let old_value = serde_json::to_value(&old_content).unwrap();
 755        let new_value = serde_json::to_value(new_content).unwrap();
 756
 757        let mut key_path = Vec::new();
 758        let mut edits = Vec::new();
 759        let tab_size = infer_json_indent_size(&text);
 760        let mut text = text.to_string();
 761        update_value_in_json_text(
 762            &mut text,
 763            &mut key_path,
 764            tab_size,
 765            &old_value,
 766            &new_value,
 767            &mut edits,
 768        );
 769        edits
 770    }
 771
 772    /// Sets the default settings via a JSON string.
 773    ///
 774    /// The string should contain a JSON object with a default value for every setting.
 775    pub fn set_default_settings(
 776        &mut self,
 777        default_settings_content: &str,
 778        cx: &mut App,
 779    ) -> Result<()> {
 780        self.default_settings =
 781            SettingsContent::parse_json_with_comments(default_settings_content)?.into();
 782        self.recompute_values(None, cx);
 783        Ok(())
 784    }
 785
 786    /// Sets the user settings via a JSON string.
 787    #[must_use]
 788    pub fn set_user_settings(
 789        &mut self,
 790        user_settings_content: &str,
 791        cx: &mut App,
 792    ) -> SettingsParseResult {
 793        let (settings, parse_result) = self.parse_and_migrate_zed_settings::<UserSettingsContent>(
 794            user_settings_content,
 795            SettingsFile::User,
 796        );
 797
 798        if let Some(settings) = settings {
 799            self.user_settings = Some(settings);
 800            self.recompute_values(None, cx);
 801        }
 802        return parse_result;
 803    }
 804
 805    /// Sets the global settings via a JSON string.
 806    #[must_use]
 807    pub fn set_global_settings(
 808        &mut self,
 809        global_settings_content: &str,
 810        cx: &mut App,
 811    ) -> SettingsParseResult {
 812        let (settings, parse_result) = self.parse_and_migrate_zed_settings::<SettingsContent>(
 813            global_settings_content,
 814            SettingsFile::Global,
 815        );
 816
 817        if let Some(settings) = settings {
 818            self.global_settings = Some(Box::new(settings));
 819            self.recompute_values(None, cx);
 820        }
 821        return parse_result;
 822    }
 823
 824    pub fn set_server_settings(
 825        &mut self,
 826        server_settings_content: &str,
 827        cx: &mut App,
 828    ) -> Result<()> {
 829        let settings = if server_settings_content.is_empty() {
 830            None
 831        } else {
 832            Option::<SettingsContent>::parse_json_with_comments(server_settings_content)?
 833        };
 834
 835        // Rewrite the server settings into a content type
 836        self.server_settings = settings.map(|settings| Box::new(settings));
 837
 838        self.recompute_values(None, cx);
 839        Ok(())
 840    }
 841
 842    /// Add or remove a set of local settings via a JSON string.
 843    pub fn set_local_settings(
 844        &mut self,
 845        root_id: WorktreeId,
 846        path: LocalSettingsPath,
 847        kind: LocalSettingsKind,
 848        settings_content: Option<&str>,
 849        cx: &mut App,
 850    ) -> std::result::Result<(), InvalidSettingsError> {
 851        let content = settings_content
 852            .map(|content| content.trim())
 853            .filter(|content| !content.is_empty());
 854        let mut zed_settings_changed = false;
 855        match (path.clone(), kind, content) {
 856            (LocalSettingsPath::InWorktree(directory_path), LocalSettingsKind::Tasks, _) => {
 857                return Err(InvalidSettingsError::Tasks {
 858                    message: "Attempted to submit tasks into the settings store".to_string(),
 859                    path: directory_path
 860                        .join(RelPath::unix(task_file_name()).unwrap())
 861                        .as_std_path()
 862                        .to_path_buf(),
 863                });
 864            }
 865            (LocalSettingsPath::InWorktree(directory_path), LocalSettingsKind::Debug, _) => {
 866                return Err(InvalidSettingsError::Debug {
 867                    message: "Attempted to submit debugger config into the settings store"
 868                        .to_string(),
 869                    path: directory_path
 870                        .join(RelPath::unix(task_file_name()).unwrap())
 871                        .as_std_path()
 872                        .to_path_buf(),
 873                });
 874            }
 875            (LocalSettingsPath::InWorktree(directory_path), LocalSettingsKind::Settings, None) => {
 876                zed_settings_changed = self
 877                    .local_settings
 878                    .remove(&(root_id, directory_path.clone()))
 879                    .is_some();
 880                self.file_errors
 881                    .remove(&SettingsFile::Project((root_id, directory_path)));
 882            }
 883            (
 884                LocalSettingsPath::InWorktree(directory_path),
 885                LocalSettingsKind::Settings,
 886                Some(settings_contents),
 887            ) => {
 888                let (new_settings, parse_result) = self
 889                    .parse_and_migrate_zed_settings::<ProjectSettingsContent>(
 890                        settings_contents,
 891                        SettingsFile::Project((root_id, directory_path.clone())),
 892                    );
 893                match parse_result.parse_status {
 894                    ParseStatus::Success => Ok(()),
 895                    ParseStatus::Failed { error } => Err(InvalidSettingsError::LocalSettings {
 896                        path: directory_path.join(local_settings_file_relative_path()),
 897                        message: error,
 898                    }),
 899                }?;
 900                if let Some(new_settings) = new_settings {
 901                    match self.local_settings.entry((root_id, directory_path)) {
 902                        btree_map::Entry::Vacant(v) => {
 903                            v.insert(SettingsContent {
 904                                project: new_settings,
 905                                ..Default::default()
 906                            });
 907                            zed_settings_changed = true;
 908                        }
 909                        btree_map::Entry::Occupied(mut o) => {
 910                            if &o.get().project != &new_settings {
 911                                o.insert(SettingsContent {
 912                                    project: new_settings,
 913                                    ..Default::default()
 914                                });
 915                                zed_settings_changed = true;
 916                            }
 917                        }
 918                    }
 919                }
 920            }
 921            (directory_path, LocalSettingsKind::Editorconfig, editorconfig_contents) => {
 922                self.editorconfig_store.update(cx, |store, _| {
 923                    store.set_configs(root_id, directory_path, editorconfig_contents)
 924                })?;
 925            }
 926            (LocalSettingsPath::OutsideWorktree(path), kind, _) => {
 927                log::error!(
 928                    "OutsideWorktree path {:?} with kind {:?} is only supported by editorconfig",
 929                    path,
 930                    kind
 931                );
 932                return Ok(());
 933            }
 934        }
 935        if let LocalSettingsPath::InWorktree(directory_path) = &path {
 936            if zed_settings_changed {
 937                self.recompute_values(Some((root_id, &directory_path)), cx);
 938            }
 939        }
 940        Ok(())
 941    }
 942
 943    pub fn set_extension_settings(
 944        &mut self,
 945        content: ExtensionsSettingsContent,
 946        cx: &mut App,
 947    ) -> Result<()> {
 948        self.extension_settings = Some(Box::new(SettingsContent {
 949            project: ProjectSettingsContent {
 950                all_languages: content.all_languages,
 951                ..Default::default()
 952            },
 953            ..Default::default()
 954        }));
 955        self.recompute_values(None, cx);
 956        Ok(())
 957    }
 958
 959    /// Add or remove a set of local settings via a JSON string.
 960    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
 961        self.local_settings
 962            .retain(|(worktree_id, _), _| worktree_id != &root_id);
 963
 964        self.editorconfig_store
 965            .update(cx, |store, _cx| store.remove_for_worktree(root_id));
 966
 967        for setting_value in self.setting_values.values_mut() {
 968            setting_value.clear_local_values(root_id);
 969        }
 970        self.recompute_values(Some((root_id, RelPath::empty())), cx);
 971        Ok(())
 972    }
 973
 974    pub fn local_settings(
 975        &self,
 976        root_id: WorktreeId,
 977    ) -> impl '_ + Iterator<Item = (Arc<RelPath>, &ProjectSettingsContent)> {
 978        self.local_settings
 979            .range(
 980                (root_id, RelPath::empty().into())
 981                    ..(
 982                        WorktreeId::from_usize(root_id.to_usize() + 1),
 983                        RelPath::empty().into(),
 984                    ),
 985            )
 986            .map(|((_, path), content)| (path.clone(), &content.project))
 987    }
 988
 989    pub fn json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
 990        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
 991            .with_transform(DefaultDenyUnknownFields)
 992            .with_transform(AllowTrailingCommas)
 993            .into_generator();
 994
 995        UserSettingsContent::json_schema(&mut generator);
 996
 997        let language_settings_content_ref = generator
 998            .subschema_for::<LanguageSettingsContent>()
 999            .to_value();
1000
1001        generator.subschema_for::<LspSettings>();
1002
1003        let lsp_settings_def = generator
1004            .definitions()
1005            .get("LspSettings")
1006            .expect("LspSettings should be defined")
1007            .clone();
1008
1009        replace_subschema::<LanguageToSettingsMap>(&mut generator, || {
1010            json_schema!({
1011                "type": "object",
1012                "properties": params
1013                    .language_names
1014                    .iter()
1015                    .map(|name| {
1016                        (
1017                            name.clone(),
1018                            language_settings_content_ref.clone(),
1019                        )
1020                    })
1021                    .collect::<serde_json::Map<_, _>>(),
1022                "errorMessage": "No language with this name is installed."
1023            })
1024        });
1025
1026        replace_subschema::<FontFamilyName>(&mut generator, || {
1027            json_schema!({
1028                "type": "string",
1029                "enum": params.font_names,
1030            })
1031        });
1032
1033        replace_subschema::<ThemeName>(&mut generator, || {
1034            json_schema!({
1035                "type": "string",
1036                "enum": params.theme_names,
1037            })
1038        });
1039
1040        replace_subschema::<IconThemeName>(&mut generator, || {
1041            json_schema!({
1042                "type": "string",
1043                "enum": params.icon_theme_names,
1044            })
1045        });
1046
1047        replace_subschema::<LspSettingsMap>(&mut generator, || {
1048            let mut lsp_properties = serde_json::Map::new();
1049
1050            for adapter_name in params.lsp_adapter_names {
1051                let mut base_lsp_settings = lsp_settings_def
1052                    .as_object()
1053                    .expect("LspSettings should be an object")
1054                    .clone();
1055
1056                if let Some(properties) = base_lsp_settings.get_mut("properties") {
1057                    if let Some(props_obj) = properties.as_object_mut() {
1058                        props_obj.insert(
1059                            "initialization_options".to_string(),
1060                            serde_json::json!({
1061                                "$ref": format!("{LSP_SETTINGS_SCHEMA_URL_PREFIX}{adapter_name}")
1062                            }),
1063                        );
1064                    }
1065                }
1066
1067                lsp_properties.insert(
1068                    adapter_name.clone(),
1069                    serde_json::Value::Object(base_lsp_settings),
1070                );
1071            }
1072
1073            json_schema!({
1074                "type": "object",
1075                "properties": lsp_properties,
1076            })
1077        });
1078
1079        generator
1080            .root_schema_for::<UserSettingsContent>()
1081            .to_value()
1082    }
1083
1084    fn recompute_values(
1085        &mut self,
1086        changed_local_path: Option<(WorktreeId, &RelPath)>,
1087        cx: &mut App,
1088    ) {
1089        // Reload the global and local values for every setting.
1090        let mut project_settings_stack = Vec::<SettingsContent>::new();
1091        let mut paths_stack = Vec::<Option<(WorktreeId, &RelPath)>>::new();
1092
1093        if changed_local_path.is_none() {
1094            let mut merged = self.default_settings.as_ref().clone();
1095            merged.merge_from_option(self.extension_settings.as_deref());
1096            merged.merge_from_option(self.global_settings.as_deref());
1097            if let Some(user_settings) = self.user_settings.as_ref() {
1098                merged.merge_from(&user_settings.content);
1099                merged.merge_from_option(user_settings.for_release_channel());
1100                merged.merge_from_option(user_settings.for_os());
1101                merged.merge_from_option(user_settings.for_profile(cx));
1102            }
1103            merged.merge_from_option(self.server_settings.as_deref());
1104            self.merged_settings = Rc::new(merged);
1105
1106            for setting_value in self.setting_values.values_mut() {
1107                let value = setting_value.from_settings(&self.merged_settings);
1108                setting_value.set_global_value(value);
1109            }
1110        }
1111
1112        for ((root_id, directory_path), local_settings) in &self.local_settings {
1113            // Build a stack of all of the local values for that setting.
1114            while let Some(prev_entry) = paths_stack.last() {
1115                if let Some((prev_root_id, prev_path)) = prev_entry
1116                    && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
1117                {
1118                    paths_stack.pop();
1119                    project_settings_stack.pop();
1120                    continue;
1121                }
1122                break;
1123            }
1124
1125            paths_stack.push(Some((*root_id, directory_path.as_ref())));
1126            let mut merged_local_settings = if let Some(deepest) = project_settings_stack.last() {
1127                (*deepest).clone()
1128            } else {
1129                self.merged_settings.as_ref().clone()
1130            };
1131            merged_local_settings.merge_from(local_settings);
1132
1133            project_settings_stack.push(merged_local_settings);
1134
1135            // If a local settings file changed, then avoid recomputing local
1136            // settings for any path outside of that directory.
1137            if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
1138                *root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
1139            }) {
1140                continue;
1141            }
1142
1143            for setting_value in self.setting_values.values_mut() {
1144                let value = setting_value.from_settings(&project_settings_stack.last().unwrap());
1145                setting_value.set_local_value(*root_id, directory_path.clone(), value);
1146            }
1147        }
1148    }
1149}
1150
1151/// The result of parsing settings, including any migration attempts
1152#[derive(Debug, Clone, PartialEq, Eq)]
1153pub struct SettingsParseResult {
1154    /// The result of parsing the settings file (possibly after migration)
1155    pub parse_status: ParseStatus,
1156    /// The result of attempting to migrate the settings file
1157    pub migration_status: MigrationStatus,
1158}
1159
1160#[derive(Debug, Clone, PartialEq, Eq)]
1161pub enum MigrationStatus {
1162    /// No migration was needed - settings are up to date
1163    NotNeeded,
1164    /// Settings were automatically migrated in memory, but the file needs to be updated
1165    Succeeded,
1166    /// Migration was attempted but failed. Original settings were parsed instead.
1167    Failed { error: String },
1168}
1169
1170impl Default for SettingsParseResult {
1171    fn default() -> Self {
1172        Self {
1173            parse_status: ParseStatus::Success,
1174            migration_status: MigrationStatus::NotNeeded,
1175        }
1176    }
1177}
1178
1179impl SettingsParseResult {
1180    pub fn unwrap(self) -> bool {
1181        self.result().unwrap()
1182    }
1183
1184    pub fn expect(self, message: &str) -> bool {
1185        self.result().expect(message)
1186    }
1187
1188    /// Formats the ParseResult as a Result type. This is a lossy conversion
1189    pub fn result(self) -> Result<bool> {
1190        let migration_result = match self.migration_status {
1191            MigrationStatus::NotNeeded => Ok(false),
1192            MigrationStatus::Succeeded => Ok(true),
1193            MigrationStatus::Failed { error } => {
1194                Err(anyhow::format_err!(error)).context("Failed to migrate settings")
1195            }
1196        };
1197
1198        let parse_result = match self.parse_status {
1199            ParseStatus::Success => Ok(()),
1200            ParseStatus::Failed { error } => {
1201                Err(anyhow::format_err!(error)).context("Failed to parse settings")
1202            }
1203        };
1204
1205        match (migration_result, parse_result) {
1206            (migration_result @ Ok(_), Ok(())) => migration_result,
1207            (Err(migration_err), Ok(())) => Err(migration_err),
1208            (_, Err(parse_err)) => Err(parse_err),
1209        }
1210    }
1211
1212    /// Returns true if there were any errors migrating and parsing the settings content or if migration was required but there were no errors
1213    pub fn requires_user_action(&self) -> bool {
1214        matches!(self.parse_status, ParseStatus::Failed { .. })
1215            || matches!(
1216                self.migration_status,
1217                MigrationStatus::Succeeded | MigrationStatus::Failed { .. }
1218            )
1219    }
1220
1221    pub fn ok(self) -> Option<bool> {
1222        self.result().ok()
1223    }
1224
1225    pub fn parse_error(&self) -> Option<String> {
1226        match &self.parse_status {
1227            ParseStatus::Failed { error } => Some(error.clone()),
1228            ParseStatus::Success => None,
1229        }
1230    }
1231}
1232
1233#[derive(Debug, Clone, PartialEq)]
1234pub enum InvalidSettingsError {
1235    LocalSettings {
1236        path: Arc<RelPath>,
1237        message: String,
1238    },
1239    UserSettings {
1240        message: String,
1241    },
1242    ServerSettings {
1243        message: String,
1244    },
1245    DefaultSettings {
1246        message: String,
1247    },
1248    Editorconfig {
1249        path: LocalSettingsPath,
1250        message: String,
1251    },
1252    Tasks {
1253        path: PathBuf,
1254        message: String,
1255    },
1256    Debug {
1257        path: PathBuf,
1258        message: String,
1259    },
1260}
1261
1262impl std::fmt::Display for InvalidSettingsError {
1263    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1264        match self {
1265            InvalidSettingsError::LocalSettings { message, .. }
1266            | InvalidSettingsError::UserSettings { message }
1267            | InvalidSettingsError::ServerSettings { message }
1268            | InvalidSettingsError::DefaultSettings { message }
1269            | InvalidSettingsError::Tasks { message, .. }
1270            | InvalidSettingsError::Editorconfig { message, .. }
1271            | InvalidSettingsError::Debug { message, .. } => {
1272                write!(f, "{message}")
1273            }
1274        }
1275    }
1276}
1277impl std::error::Error for InvalidSettingsError {}
1278
1279impl Debug for SettingsStore {
1280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1281        f.debug_struct("SettingsStore")
1282            .field(
1283                "types",
1284                &self
1285                    .setting_values
1286                    .values()
1287                    .map(|value| value.setting_type_name())
1288                    .collect::<Vec<_>>(),
1289            )
1290            .field("default_settings", &self.default_settings)
1291            .field("user_settings", &self.user_settings)
1292            .field("local_settings", &self.local_settings)
1293            .finish_non_exhaustive()
1294    }
1295}
1296
1297impl<T: Settings> AnySettingValue for SettingValue<T> {
1298    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any> {
1299        Box::new(T::from_settings(s)) as _
1300    }
1301
1302    fn setting_type_name(&self) -> &'static str {
1303        type_name::<T>()
1304    }
1305
1306    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)> {
1307        self.local_values
1308            .iter()
1309            .map(|(id, path, value)| (*id, path.clone(), value as _))
1310            .collect()
1311    }
1312
1313    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1314        if let Some(SettingsLocation { worktree_id, path }) = path {
1315            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1316                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1317                    return value;
1318                }
1319            }
1320        }
1321
1322        self.global_value
1323            .as_ref()
1324            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1325    }
1326
1327    fn set_global_value(&mut self, value: Box<dyn Any>) {
1328        self.global_value = Some(*value.downcast().unwrap());
1329    }
1330
1331    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<RelPath>, value: Box<dyn Any>) {
1332        let value = *value.downcast().unwrap();
1333        match self
1334            .local_values
1335            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1336        {
1337            Ok(ix) => self.local_values[ix].2 = value,
1338            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1339        }
1340    }
1341
1342    fn clear_local_values(&mut self, root_id: WorktreeId) {
1343        self.local_values
1344            .retain(|(worktree_id, _, _)| *worktree_id != root_id);
1345    }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350    use std::num::NonZeroU32;
1351
1352    use crate::{
1353        ClosePosition, ItemSettingsContent, VsCodeSettingsSource, default_settings,
1354        settings_content::LanguageSettingsContent, test_settings,
1355    };
1356
1357    use super::*;
1358    use unindent::Unindent;
1359    use util::rel_path::rel_path;
1360
1361    #[derive(Debug, PartialEq)]
1362    struct AutoUpdateSetting {
1363        auto_update: bool,
1364    }
1365
1366    impl Settings for AutoUpdateSetting {
1367        fn from_settings(content: &SettingsContent) -> Self {
1368            AutoUpdateSetting {
1369                auto_update: content.auto_update.unwrap(),
1370            }
1371        }
1372    }
1373
1374    #[derive(Debug, PartialEq)]
1375    struct ItemSettings {
1376        close_position: ClosePosition,
1377        git_status: bool,
1378    }
1379
1380    impl Settings for ItemSettings {
1381        fn from_settings(content: &SettingsContent) -> Self {
1382            let content = content.tabs.clone().unwrap();
1383            ItemSettings {
1384                close_position: content.close_position.unwrap(),
1385                git_status: content.git_status.unwrap(),
1386            }
1387        }
1388    }
1389
1390    #[derive(Debug, PartialEq)]
1391    struct DefaultLanguageSettings {
1392        tab_size: NonZeroU32,
1393        preferred_line_length: u32,
1394    }
1395
1396    impl Settings for DefaultLanguageSettings {
1397        fn from_settings(content: &SettingsContent) -> Self {
1398            let content = &content.project.all_languages.defaults;
1399            DefaultLanguageSettings {
1400                tab_size: content.tab_size.unwrap(),
1401                preferred_line_length: content.preferred_line_length.unwrap(),
1402            }
1403        }
1404    }
1405
1406    #[derive(Debug, PartialEq)]
1407    struct ThemeSettings {
1408        buffer_font_family: FontFamilyName,
1409        buffer_font_fallbacks: Vec<FontFamilyName>,
1410    }
1411
1412    impl Settings for ThemeSettings {
1413        fn from_settings(content: &SettingsContent) -> Self {
1414            let content = content.theme.clone();
1415            ThemeSettings {
1416                buffer_font_family: content.buffer_font_family.unwrap(),
1417                buffer_font_fallbacks: content.buffer_font_fallbacks.unwrap(),
1418            }
1419        }
1420    }
1421
1422    #[gpui::test]
1423    fn test_settings_store_basic(cx: &mut App) {
1424        let mut store = SettingsStore::new(cx, &default_settings());
1425        store.register_setting::<AutoUpdateSetting>();
1426        store.register_setting::<ItemSettings>();
1427        store.register_setting::<DefaultLanguageSettings>();
1428
1429        assert_eq!(
1430            store.get::<AutoUpdateSetting>(None),
1431            &AutoUpdateSetting { auto_update: true }
1432        );
1433        assert_eq!(
1434            store.get::<ItemSettings>(None).close_position,
1435            ClosePosition::Right
1436        );
1437
1438        store
1439            .set_user_settings(
1440                r#"{
1441                    "auto_update": false,
1442                    "tabs": {
1443                      "close_position": "left"
1444                    }
1445                }"#,
1446                cx,
1447            )
1448            .unwrap();
1449
1450        assert_eq!(
1451            store.get::<AutoUpdateSetting>(None),
1452            &AutoUpdateSetting { auto_update: false }
1453        );
1454        assert_eq!(
1455            store.get::<ItemSettings>(None).close_position,
1456            ClosePosition::Left
1457        );
1458
1459        store
1460            .set_local_settings(
1461                WorktreeId::from_usize(1),
1462                LocalSettingsPath::InWorktree(rel_path("root1").into()),
1463                LocalSettingsKind::Settings,
1464                Some(r#"{ "tab_size": 5 }"#),
1465                cx,
1466            )
1467            .unwrap();
1468        store
1469            .set_local_settings(
1470                WorktreeId::from_usize(1),
1471                LocalSettingsPath::InWorktree(rel_path("root1/subdir").into()),
1472                LocalSettingsKind::Settings,
1473                Some(r#"{ "preferred_line_length": 50 }"#),
1474                cx,
1475            )
1476            .unwrap();
1477
1478        store
1479            .set_local_settings(
1480                WorktreeId::from_usize(1),
1481                LocalSettingsPath::InWorktree(rel_path("root2").into()),
1482                LocalSettingsKind::Settings,
1483                Some(r#"{ "tab_size": 9, "auto_update": true}"#),
1484                cx,
1485            )
1486            .unwrap();
1487
1488        assert_eq!(
1489            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1490                worktree_id: WorktreeId::from_usize(1),
1491                path: rel_path("root1/something"),
1492            })),
1493            &DefaultLanguageSettings {
1494                preferred_line_length: 80,
1495                tab_size: 5.try_into().unwrap(),
1496            }
1497        );
1498        assert_eq!(
1499            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1500                worktree_id: WorktreeId::from_usize(1),
1501                path: rel_path("root1/subdir/something"),
1502            })),
1503            &DefaultLanguageSettings {
1504                preferred_line_length: 50,
1505                tab_size: 5.try_into().unwrap(),
1506            }
1507        );
1508        assert_eq!(
1509            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1510                worktree_id: WorktreeId::from_usize(1),
1511                path: rel_path("root2/something"),
1512            })),
1513            &DefaultLanguageSettings {
1514                preferred_line_length: 80,
1515                tab_size: 9.try_into().unwrap(),
1516            }
1517        );
1518        assert_eq!(
1519            store.get::<AutoUpdateSetting>(Some(SettingsLocation {
1520                worktree_id: WorktreeId::from_usize(1),
1521                path: rel_path("root2/something")
1522            })),
1523            &AutoUpdateSetting { auto_update: false }
1524        );
1525    }
1526
1527    #[gpui::test]
1528    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1529        let mut store = SettingsStore::new(cx, &test_settings());
1530        store
1531            .set_user_settings(r#"{ "auto_update": false }"#, cx)
1532            .unwrap();
1533        store.register_setting::<AutoUpdateSetting>();
1534
1535        assert_eq!(
1536            store.get::<AutoUpdateSetting>(None),
1537            &AutoUpdateSetting { auto_update: false }
1538        );
1539    }
1540
1541    #[track_caller]
1542    fn check_settings_update(
1543        store: &mut SettingsStore,
1544        old_json: String,
1545        update: fn(&mut SettingsContent),
1546        expected_new_json: String,
1547        cx: &mut App,
1548    ) {
1549        store.set_user_settings(&old_json, cx).ok();
1550        let edits = store.edits_for_update(&old_json, update);
1551        let mut new_json = old_json;
1552        for (range, replacement) in edits.into_iter() {
1553            new_json.replace_range(range, &replacement);
1554        }
1555        pretty_assertions::assert_eq!(new_json, expected_new_json);
1556    }
1557
1558    #[gpui::test]
1559    fn test_setting_store_update(cx: &mut App) {
1560        let mut store = SettingsStore::new(cx, &test_settings());
1561
1562        // entries added and updated
1563        check_settings_update(
1564            &mut store,
1565            r#"{
1566                "languages": {
1567                    "JSON": {
1568                        "auto_indent": true
1569                    }
1570                }
1571            }"#
1572            .unindent(),
1573            |settings| {
1574                settings
1575                    .languages_mut()
1576                    .get_mut("JSON")
1577                    .unwrap()
1578                    .auto_indent = Some(false);
1579
1580                settings.languages_mut().insert(
1581                    "Rust".into(),
1582                    LanguageSettingsContent {
1583                        auto_indent: Some(true),
1584                        ..Default::default()
1585                    },
1586                );
1587            },
1588            r#"{
1589                "languages": {
1590                    "Rust": {
1591                        "auto_indent": true
1592                    },
1593                    "JSON": {
1594                        "auto_indent": false
1595                    }
1596                }
1597            }"#
1598            .unindent(),
1599            cx,
1600        );
1601
1602        // entries removed
1603        check_settings_update(
1604            &mut store,
1605            r#"{
1606                "languages": {
1607                    "Rust": {
1608                        "language_setting_2": true
1609                    },
1610                    "JSON": {
1611                        "language_setting_1": false
1612                    }
1613                }
1614            }"#
1615            .unindent(),
1616            |settings| {
1617                settings.languages_mut().remove("JSON").unwrap();
1618            },
1619            r#"{
1620                "languages": {
1621                    "Rust": {
1622                        "language_setting_2": true
1623                    }
1624                }
1625            }"#
1626            .unindent(),
1627            cx,
1628        );
1629
1630        check_settings_update(
1631            &mut store,
1632            r#"{
1633                "languages": {
1634                    "Rust": {
1635                        "language_setting_2": true
1636                    },
1637                    "JSON": {
1638                        "language_setting_1": false
1639                    }
1640                }
1641            }"#
1642            .unindent(),
1643            |settings| {
1644                settings.languages_mut().remove("Rust").unwrap();
1645            },
1646            r#"{
1647                "languages": {
1648                    "JSON": {
1649                        "language_setting_1": false
1650                    }
1651                }
1652            }"#
1653            .unindent(),
1654            cx,
1655        );
1656
1657        // weird formatting
1658        check_settings_update(
1659            &mut store,
1660            r#"{
1661                "tabs":   { "close_position": "left", "name": "Max"  }
1662                }"#
1663            .unindent(),
1664            |settings| {
1665                settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left);
1666            },
1667            r#"{
1668                "tabs":   { "close_position": "left", "name": "Max"  }
1669                }"#
1670            .unindent(),
1671            cx,
1672        );
1673
1674        // single-line formatting, other keys
1675        check_settings_update(
1676            &mut store,
1677            r#"{ "one": 1, "two": 2 }"#.to_owned(),
1678            |settings| settings.auto_update = Some(true),
1679            r#"{ "auto_update": true, "one": 1, "two": 2 }"#.to_owned(),
1680            cx,
1681        );
1682
1683        // empty object
1684        check_settings_update(
1685            &mut store,
1686            r#"{
1687                "tabs": {}
1688            }"#
1689            .unindent(),
1690            |settings| settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left),
1691            r#"{
1692                "tabs": {
1693                    "close_position": "left"
1694                }
1695            }"#
1696            .unindent(),
1697            cx,
1698        );
1699
1700        // no content
1701        check_settings_update(
1702            &mut store,
1703            r#""#.unindent(),
1704            |settings| {
1705                settings.tabs = Some(ItemSettingsContent {
1706                    git_status: Some(true),
1707                    ..Default::default()
1708                })
1709            },
1710            r#"{
1711              "tabs": {
1712                "git_status": true
1713              }
1714            }
1715            "#
1716            .unindent(),
1717            cx,
1718        );
1719
1720        check_settings_update(
1721            &mut store,
1722            r#"{
1723            }
1724            "#
1725            .unindent(),
1726            |settings| settings.title_bar.get_or_insert_default().show_branch_name = Some(true),
1727            r#"{
1728              "title_bar": {
1729                "show_branch_name": true
1730              }
1731            }
1732            "#
1733            .unindent(),
1734            cx,
1735        );
1736    }
1737
1738    #[gpui::test]
1739    fn test_vscode_import(cx: &mut App) {
1740        let mut store = SettingsStore::new(cx, &test_settings());
1741        store.register_setting::<DefaultLanguageSettings>();
1742        store.register_setting::<ItemSettings>();
1743        store.register_setting::<AutoUpdateSetting>();
1744        store.register_setting::<ThemeSettings>();
1745
1746        // create settings that werent present
1747        check_vscode_import(
1748            &mut store,
1749            r#"{
1750            }
1751            "#
1752            .unindent(),
1753            r#" { "editor.tabSize": 37 } "#.to_owned(),
1754            r#"{
1755              "base_keymap": "VSCode",
1756              "tab_size": 37
1757            }
1758            "#
1759            .unindent(),
1760            cx,
1761        );
1762
1763        // persist settings that were present
1764        check_vscode_import(
1765            &mut store,
1766            r#"{
1767                "preferred_line_length": 99,
1768            }
1769            "#
1770            .unindent(),
1771            r#"{ "editor.tabSize": 42 }"#.to_owned(),
1772            r#"{
1773                "base_keymap": "VSCode",
1774                "tab_size": 42,
1775                "preferred_line_length": 99,
1776            }
1777            "#
1778            .unindent(),
1779            cx,
1780        );
1781
1782        // don't clobber settings that aren't present in vscode
1783        check_vscode_import(
1784            &mut store,
1785            r#"{
1786                "preferred_line_length": 99,
1787                "tab_size": 42
1788            }
1789            "#
1790            .unindent(),
1791            r#"{}"#.to_owned(),
1792            r#"{
1793                "base_keymap": "VSCode",
1794                "preferred_line_length": 99,
1795                "tab_size": 42
1796            }
1797            "#
1798            .unindent(),
1799            cx,
1800        );
1801
1802        // custom enum
1803        check_vscode_import(
1804            &mut store,
1805            r#"{
1806            }
1807            "#
1808            .unindent(),
1809            r#"{ "git.decorations.enabled": true }"#.to_owned(),
1810            r#"{
1811              "project_panel": {
1812                "git_status": true
1813              },
1814              "outline_panel": {
1815                "git_status": true
1816              },
1817              "base_keymap": "VSCode",
1818              "tabs": {
1819                "git_status": true
1820              }
1821            }
1822            "#
1823            .unindent(),
1824            cx,
1825        );
1826
1827        // font-family
1828        check_vscode_import(
1829            &mut store,
1830            r#"{
1831            }
1832            "#
1833            .unindent(),
1834            r#"{ "editor.fontFamily": "Cascadia Code, 'Consolas', Courier New" }"#.to_owned(),
1835            r#"{
1836              "base_keymap": "VSCode",
1837              "buffer_font_fallbacks": [
1838                "Consolas",
1839                "Courier New"
1840              ],
1841              "buffer_font_family": "Cascadia Code"
1842            }
1843            "#
1844            .unindent(),
1845            cx,
1846        );
1847    }
1848
1849    #[track_caller]
1850    fn check_vscode_import(
1851        store: &mut SettingsStore,
1852        old: String,
1853        vscode: String,
1854        expected: String,
1855        cx: &mut App,
1856    ) {
1857        store.set_user_settings(&old, cx).ok();
1858        let new = store.get_vscode_edits(
1859            old,
1860            &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
1861        );
1862        pretty_assertions::assert_eq!(new, expected);
1863    }
1864
1865    #[gpui::test]
1866    fn test_update_git_settings(cx: &mut App) {
1867        let store = SettingsStore::new(cx, &test_settings());
1868
1869        let actual = store.new_text_for_update("{}".to_string(), |current| {
1870            current
1871                .git
1872                .get_or_insert_default()
1873                .inline_blame
1874                .get_or_insert_default()
1875                .enabled = Some(true);
1876        });
1877        pretty_assertions::assert_str_eq!(
1878            actual,
1879            r#"{
1880              "git": {
1881                "inline_blame": {
1882                  "enabled": true
1883                }
1884              }
1885            }
1886            "#
1887            .unindent()
1888        );
1889    }
1890
1891    #[gpui::test]
1892    fn test_global_settings(cx: &mut App) {
1893        let mut store = SettingsStore::new(cx, &test_settings());
1894        store.register_setting::<ItemSettings>();
1895
1896        // Set global settings - these should override defaults but not user settings
1897        store
1898            .set_global_settings(
1899                r#"{
1900                    "tabs": {
1901                        "close_position": "right",
1902                        "git_status": true,
1903                    }
1904                }"#,
1905                cx,
1906            )
1907            .unwrap();
1908
1909        // Before user settings, global settings should apply
1910        assert_eq!(
1911            store.get::<ItemSettings>(None),
1912            &ItemSettings {
1913                close_position: ClosePosition::Right,
1914                git_status: true,
1915            }
1916        );
1917
1918        // Set user settings - these should override both defaults and global
1919        store
1920            .set_user_settings(
1921                r#"{
1922                    "tabs": {
1923                        "close_position": "left"
1924                    }
1925                }"#,
1926                cx,
1927            )
1928            .unwrap();
1929
1930        // User settings should override global settings
1931        assert_eq!(
1932            store.get::<ItemSettings>(None),
1933            &ItemSettings {
1934                close_position: ClosePosition::Left,
1935                git_status: true, // Staff from global settings
1936            }
1937        );
1938    }
1939
1940    #[gpui::test]
1941    fn test_get_value_for_field_basic(cx: &mut App) {
1942        let mut store = SettingsStore::new(cx, &test_settings());
1943        store.register_setting::<DefaultLanguageSettings>();
1944
1945        store
1946            .set_user_settings(r#"{"preferred_line_length": 0}"#, cx)
1947            .unwrap();
1948        let local = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
1949        store
1950            .set_local_settings(
1951                local.0,
1952                LocalSettingsPath::InWorktree(local.1.clone()),
1953                LocalSettingsKind::Settings,
1954                Some(r#"{}"#),
1955                cx,
1956            )
1957            .unwrap();
1958
1959        fn get(content: &SettingsContent) -> Option<&u32> {
1960            content
1961                .project
1962                .all_languages
1963                .defaults
1964                .preferred_line_length
1965                .as_ref()
1966        }
1967
1968        let default_value = *get(&store.default_settings).unwrap();
1969
1970        assert_eq!(
1971            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1972            (SettingsFile::User, Some(&0))
1973        );
1974        assert_eq!(
1975            store.get_value_from_file(SettingsFile::User, get),
1976            (SettingsFile::User, Some(&0))
1977        );
1978        store.set_user_settings(r#"{}"#, cx).unwrap();
1979        assert_eq!(
1980            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1981            (SettingsFile::Default, Some(&default_value))
1982        );
1983        store
1984            .set_local_settings(
1985                local.0,
1986                LocalSettingsPath::InWorktree(local.1.clone()),
1987                LocalSettingsKind::Settings,
1988                Some(r#"{"preferred_line_length": 80}"#),
1989                cx,
1990            )
1991            .unwrap();
1992        assert_eq!(
1993            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1994            (SettingsFile::Project(local), Some(&80))
1995        );
1996        assert_eq!(
1997            store.get_value_from_file(SettingsFile::User, get),
1998            (SettingsFile::Default, Some(&default_value))
1999        );
2000    }
2001
2002    #[gpui::test]
2003    fn test_get_value_for_field_local_worktrees_dont_interfere(cx: &mut App) {
2004        let mut store = SettingsStore::new(cx, &test_settings());
2005        store.register_setting::<DefaultLanguageSettings>();
2006        store.register_setting::<AutoUpdateSetting>();
2007
2008        let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
2009
2010        let local_1_child = (
2011            WorktreeId::from_usize(0),
2012            RelPath::new(
2013                std::path::Path::new("child1"),
2014                util::paths::PathStyle::Posix,
2015            )
2016            .unwrap()
2017            .into_arc(),
2018        );
2019
2020        let local_2 = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
2021        let local_2_child = (
2022            WorktreeId::from_usize(1),
2023            RelPath::new(
2024                std::path::Path::new("child2"),
2025                util::paths::PathStyle::Posix,
2026            )
2027            .unwrap()
2028            .into_arc(),
2029        );
2030
2031        fn get(content: &SettingsContent) -> Option<&u32> {
2032            content
2033                .project
2034                .all_languages
2035                .defaults
2036                .preferred_line_length
2037                .as_ref()
2038        }
2039
2040        store
2041            .set_local_settings(
2042                local_1.0,
2043                LocalSettingsPath::InWorktree(local_1.1.clone()),
2044                LocalSettingsKind::Settings,
2045                Some(r#"{"preferred_line_length": 1}"#),
2046                cx,
2047            )
2048            .unwrap();
2049        store
2050            .set_local_settings(
2051                local_1_child.0,
2052                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2053                LocalSettingsKind::Settings,
2054                Some(r#"{}"#),
2055                cx,
2056            )
2057            .unwrap();
2058        store
2059            .set_local_settings(
2060                local_2.0,
2061                LocalSettingsPath::InWorktree(local_2.1.clone()),
2062                LocalSettingsKind::Settings,
2063                Some(r#"{"preferred_line_length": 2}"#),
2064                cx,
2065            )
2066            .unwrap();
2067        store
2068            .set_local_settings(
2069                local_2_child.0,
2070                LocalSettingsPath::InWorktree(local_2_child.1.clone()),
2071                LocalSettingsKind::Settings,
2072                Some(r#"{}"#),
2073                cx,
2074            )
2075            .unwrap();
2076
2077        // each local child should only inherit from it's parent
2078        assert_eq!(
2079            store.get_value_from_file(SettingsFile::Project(local_2_child), get),
2080            (SettingsFile::Project(local_2), Some(&2))
2081        );
2082        assert_eq!(
2083            store.get_value_from_file(SettingsFile::Project(local_1_child.clone()), get),
2084            (SettingsFile::Project(local_1.clone()), Some(&1))
2085        );
2086
2087        // adjacent children should be treated as siblings not inherit from each other
2088        let local_1_adjacent_child = (local_1.0, rel_path("adjacent_child").into_arc());
2089        store
2090            .set_local_settings(
2091                local_1_adjacent_child.0,
2092                LocalSettingsPath::InWorktree(local_1_adjacent_child.1.clone()),
2093                LocalSettingsKind::Settings,
2094                Some(r#"{}"#),
2095                cx,
2096            )
2097            .unwrap();
2098        store
2099            .set_local_settings(
2100                local_1_child.0,
2101                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2102                LocalSettingsKind::Settings,
2103                Some(r#"{"preferred_line_length": 3}"#),
2104                cx,
2105            )
2106            .unwrap();
2107
2108        assert_eq!(
2109            store.get_value_from_file(SettingsFile::Project(local_1_adjacent_child.clone()), get),
2110            (SettingsFile::Project(local_1.clone()), Some(&1))
2111        );
2112        store
2113            .set_local_settings(
2114                local_1_adjacent_child.0,
2115                LocalSettingsPath::InWorktree(local_1_adjacent_child.1),
2116                LocalSettingsKind::Settings,
2117                Some(r#"{"preferred_line_length": 3}"#),
2118                cx,
2119            )
2120            .unwrap();
2121        store
2122            .set_local_settings(
2123                local_1_child.0,
2124                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2125                LocalSettingsKind::Settings,
2126                Some(r#"{}"#),
2127                cx,
2128            )
2129            .unwrap();
2130        assert_eq!(
2131            store.get_value_from_file(SettingsFile::Project(local_1_child), get),
2132            (SettingsFile::Project(local_1), Some(&1))
2133        );
2134    }
2135
2136    #[gpui::test]
2137    fn test_get_overrides_for_field(cx: &mut App) {
2138        let mut store = SettingsStore::new(cx, &test_settings());
2139        store.register_setting::<DefaultLanguageSettings>();
2140
2141        let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
2142        let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc());
2143        let wt0_child2 = (WorktreeId::from_usize(0), rel_path("child2").into_arc());
2144
2145        let wt1_root = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
2146        let wt1_subdir = (WorktreeId::from_usize(1), rel_path("subdir").into_arc());
2147
2148        fn get(content: &SettingsContent) -> &Option<u32> {
2149            &content.project.all_languages.defaults.preferred_line_length
2150        }
2151
2152        store
2153            .set_user_settings(r#"{"preferred_line_length": 100}"#, cx)
2154            .unwrap();
2155
2156        store
2157            .set_local_settings(
2158                wt0_root.0,
2159                LocalSettingsPath::InWorktree(wt0_root.1.clone()),
2160                LocalSettingsKind::Settings,
2161                Some(r#"{"preferred_line_length": 80}"#),
2162                cx,
2163            )
2164            .unwrap();
2165        store
2166            .set_local_settings(
2167                wt0_child1.0,
2168                LocalSettingsPath::InWorktree(wt0_child1.1.clone()),
2169                LocalSettingsKind::Settings,
2170                Some(r#"{"preferred_line_length": 120}"#),
2171                cx,
2172            )
2173            .unwrap();
2174        store
2175            .set_local_settings(
2176                wt0_child2.0,
2177                LocalSettingsPath::InWorktree(wt0_child2.1.clone()),
2178                LocalSettingsKind::Settings,
2179                Some(r#"{}"#),
2180                cx,
2181            )
2182            .unwrap();
2183
2184        store
2185            .set_local_settings(
2186                wt1_root.0,
2187                LocalSettingsPath::InWorktree(wt1_root.1.clone()),
2188                LocalSettingsKind::Settings,
2189                Some(r#"{"preferred_line_length": 90}"#),
2190                cx,
2191            )
2192            .unwrap();
2193        store
2194            .set_local_settings(
2195                wt1_subdir.0,
2196                LocalSettingsPath::InWorktree(wt1_subdir.1.clone()),
2197                LocalSettingsKind::Settings,
2198                Some(r#"{}"#),
2199                cx,
2200            )
2201            .unwrap();
2202
2203        let overrides = store.get_overrides_for_field(SettingsFile::Default, get);
2204        assert_eq!(
2205            overrides,
2206            vec![
2207                SettingsFile::User,
2208                SettingsFile::Project(wt0_root.clone()),
2209                SettingsFile::Project(wt0_child1.clone()),
2210                SettingsFile::Project(wt1_root.clone()),
2211            ]
2212        );
2213
2214        let overrides = store.get_overrides_for_field(SettingsFile::User, get);
2215        assert_eq!(
2216            overrides,
2217            vec![
2218                SettingsFile::Project(wt0_root.clone()),
2219                SettingsFile::Project(wt0_child1.clone()),
2220                SettingsFile::Project(wt1_root.clone()),
2221            ]
2222        );
2223
2224        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_root), get);
2225        assert_eq!(overrides, vec![]);
2226
2227        let overrides =
2228            store.get_overrides_for_field(SettingsFile::Project(wt0_child1.clone()), get);
2229        assert_eq!(overrides, vec![]);
2230
2231        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child2), get);
2232        assert_eq!(overrides, vec![]);
2233
2234        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_root), get);
2235        assert_eq!(overrides, vec![]);
2236
2237        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_subdir), get);
2238        assert_eq!(overrides, vec![]);
2239
2240        let wt0_deep_child = (
2241            WorktreeId::from_usize(0),
2242            rel_path("child1/subdir").into_arc(),
2243        );
2244        store
2245            .set_local_settings(
2246                wt0_deep_child.0,
2247                LocalSettingsPath::InWorktree(wt0_deep_child.1.clone()),
2248                LocalSettingsKind::Settings,
2249                Some(r#"{"preferred_line_length": 140}"#),
2250                cx,
2251            )
2252            .unwrap();
2253
2254        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_deep_child), get);
2255        assert_eq!(overrides, vec![]);
2256
2257        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child1), get);
2258        assert_eq!(overrides, vec![]);
2259    }
2260
2261    #[test]
2262    fn test_file_ord() {
2263        let wt0_root =
2264            SettingsFile::Project((WorktreeId::from_usize(0), RelPath::empty().into_arc()));
2265        let wt0_child1 =
2266            SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child1").into_arc()));
2267        let wt0_child2 =
2268            SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child2").into_arc()));
2269
2270        let wt1_root =
2271            SettingsFile::Project((WorktreeId::from_usize(1), RelPath::empty().into_arc()));
2272        let wt1_subdir =
2273            SettingsFile::Project((WorktreeId::from_usize(1), rel_path("subdir").into_arc()));
2274
2275        let mut files = vec![
2276            &wt1_root,
2277            &SettingsFile::Default,
2278            &wt0_root,
2279            &wt1_subdir,
2280            &wt0_child2,
2281            &SettingsFile::Server,
2282            &wt0_child1,
2283            &SettingsFile::User,
2284        ];
2285
2286        files.sort();
2287        pretty_assertions::assert_eq!(
2288            files,
2289            vec![
2290                &wt0_child2,
2291                &wt0_child1,
2292                &wt0_root,
2293                &wt1_subdir,
2294                &wt1_root,
2295                &SettingsFile::Server,
2296                &SettingsFile::User,
2297                &SettingsFile::Default,
2298            ]
2299        )
2300    }
2301
2302    #[gpui::test]
2303    fn test_lsp_settings_schema_generation(cx: &mut App) {
2304        let store = SettingsStore::test(cx);
2305
2306        let schema = store.json_schema(&SettingsJsonSchemaParams {
2307            language_names: &["Rust".to_string(), "TypeScript".to_string()],
2308            font_names: &["Zed Mono".to_string()],
2309            theme_names: &["One Dark".into()],
2310            icon_theme_names: &["Zed Icons".into()],
2311            lsp_adapter_names: &[
2312                "rust-analyzer".to_string(),
2313                "typescript-language-server".to_string(),
2314            ],
2315        });
2316
2317        let properties = schema
2318            .pointer("/$defs/LspSettingsMap/properties")
2319            .expect("LspSettingsMap should have properties")
2320            .as_object()
2321            .unwrap();
2322
2323        assert!(properties.contains_key("rust-analyzer"));
2324        assert!(properties.contains_key("typescript-language-server"));
2325
2326        let init_options_ref = properties
2327            .get("rust-analyzer")
2328            .unwrap()
2329            .pointer("/properties/initialization_options/$ref")
2330            .expect("initialization_options should have a $ref")
2331            .as_str()
2332            .unwrap();
2333
2334        assert_eq!(init_options_ref, "zed://schemas/settings/lsp/rust-analyzer");
2335    }
2336}