settings_store.rs

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