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