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    /// Configures common schema replacements shared between user and project
 993    /// settings schemas.
 994    ///
 995    /// This sets up language-specific settings and LSP adapter settings that
 996    /// are valid in both user and project settings.
 997    fn configure_schema_generator(
 998        generator: &mut schemars::SchemaGenerator,
 999        params: &SettingsJsonSchemaParams,
1000    ) {
1001        let language_settings_content_ref = generator
1002            .subschema_for::<LanguageSettingsContent>()
1003            .to_value();
1004
1005        replace_subschema::<LanguageToSettingsMap>(generator, || {
1006            json_schema!({
1007                "type": "object",
1008                "errorMessage": "No language with this name is installed.",
1009                "properties": params.language_names.iter().map(|name| (name.clone(), language_settings_content_ref.clone())).collect::<serde_json::Map<_, _>>()
1010            })
1011        });
1012
1013        generator.subschema_for::<LspSettings>();
1014
1015        let lsp_settings_definition = generator
1016            .definitions()
1017            .get("LspSettings")
1018            .expect("LspSettings should be defined")
1019            .clone();
1020
1021        replace_subschema::<LspSettingsMap>(generator, || {
1022            let mut lsp_properties = serde_json::Map::new();
1023
1024            for adapter_name in params.lsp_adapter_names {
1025                let mut base_lsp_settings = lsp_settings_definition
1026                    .as_object()
1027                    .expect("LspSettings should be an object")
1028                    .clone();
1029
1030                if let Some(properties) = base_lsp_settings.get_mut("properties") {
1031                    if let Some(properties_object) = properties.as_object_mut() {
1032                        properties_object.insert(
1033                            "initialization_options".to_string(),
1034                            serde_json::json!({
1035                                "$ref": format!("{LSP_SETTINGS_SCHEMA_URL_PREFIX}{adapter_name}")
1036                            }),
1037                        );
1038                    }
1039                }
1040
1041                lsp_properties.insert(
1042                    adapter_name.clone(),
1043                    serde_json::Value::Object(base_lsp_settings),
1044                );
1045            }
1046
1047            json_schema!({
1048                "type": "object",
1049                "properties": lsp_properties
1050            })
1051        });
1052    }
1053
1054    pub fn json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
1055        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
1056            .with_transform(DefaultDenyUnknownFields)
1057            .with_transform(AllowTrailingCommas)
1058            .into_generator();
1059
1060        UserSettingsContent::json_schema(&mut generator);
1061        Self::configure_schema_generator(&mut generator, params);
1062
1063        replace_subschema::<FontFamilyName>(&mut generator, || {
1064            json_schema!({
1065                "type": "string",
1066                "enum": params.font_names,
1067            })
1068        });
1069
1070        replace_subschema::<ThemeName>(&mut generator, || {
1071            json_schema!({
1072                "type": "string",
1073                "enum": params.theme_names,
1074            })
1075        });
1076
1077        replace_subschema::<IconThemeName>(&mut generator, || {
1078            json_schema!({
1079                "type": "string",
1080                "enum": params.icon_theme_names,
1081            })
1082        });
1083
1084        generator
1085            .root_schema_for::<UserSettingsContent>()
1086            .to_value()
1087    }
1088
1089    /// Generate JSON schema for project settings, including only settings valid
1090    /// for project-level configurations.
1091    pub fn project_json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
1092        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
1093            .with_transform(DefaultDenyUnknownFields)
1094            .with_transform(AllowTrailingCommas)
1095            .into_generator();
1096
1097        ProjectSettingsContent::json_schema(&mut generator);
1098        Self::configure_schema_generator(&mut generator, params);
1099
1100        generator
1101            .root_schema_for::<ProjectSettingsContent>()
1102            .to_value()
1103    }
1104
1105    fn recompute_values(
1106        &mut self,
1107        changed_local_path: Option<(WorktreeId, &RelPath)>,
1108        cx: &mut App,
1109    ) {
1110        // Reload the global and local values for every setting.
1111        let mut project_settings_stack = Vec::<SettingsContent>::new();
1112        let mut paths_stack = Vec::<Option<(WorktreeId, &RelPath)>>::new();
1113
1114        if changed_local_path.is_none() {
1115            let mut merged = self.default_settings.as_ref().clone();
1116            merged.merge_from_option(self.extension_settings.as_deref());
1117            merged.merge_from_option(self.global_settings.as_deref());
1118            if let Some(user_settings) = self.user_settings.as_ref() {
1119                merged.merge_from(&user_settings.content);
1120                merged.merge_from_option(user_settings.for_release_channel());
1121                merged.merge_from_option(user_settings.for_os());
1122                merged.merge_from_option(user_settings.for_profile(cx));
1123            }
1124            merged.merge_from_option(self.server_settings.as_deref());
1125            self.merged_settings = Rc::new(merged);
1126
1127            for setting_value in self.setting_values.values_mut() {
1128                let value = setting_value.from_settings(&self.merged_settings);
1129                setting_value.set_global_value(value);
1130            }
1131        }
1132
1133        for ((root_id, directory_path), local_settings) in &self.local_settings {
1134            // Build a stack of all of the local values for that setting.
1135            while let Some(prev_entry) = paths_stack.last() {
1136                if let Some((prev_root_id, prev_path)) = prev_entry
1137                    && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
1138                {
1139                    paths_stack.pop();
1140                    project_settings_stack.pop();
1141                    continue;
1142                }
1143                break;
1144            }
1145
1146            paths_stack.push(Some((*root_id, directory_path.as_ref())));
1147            let mut merged_local_settings = if let Some(deepest) = project_settings_stack.last() {
1148                (*deepest).clone()
1149            } else {
1150                self.merged_settings.as_ref().clone()
1151            };
1152            merged_local_settings.merge_from(local_settings);
1153
1154            project_settings_stack.push(merged_local_settings);
1155
1156            // If a local settings file changed, then avoid recomputing local
1157            // settings for any path outside of that directory.
1158            if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
1159                *root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
1160            }) {
1161                continue;
1162            }
1163
1164            for setting_value in self.setting_values.values_mut() {
1165                let value = setting_value.from_settings(&project_settings_stack.last().unwrap());
1166                setting_value.set_local_value(*root_id, directory_path.clone(), value);
1167            }
1168        }
1169    }
1170}
1171
1172/// The result of parsing settings, including any migration attempts
1173#[derive(Debug, Clone, PartialEq, Eq)]
1174pub struct SettingsParseResult {
1175    /// The result of parsing the settings file (possibly after migration)
1176    pub parse_status: ParseStatus,
1177    /// The result of attempting to migrate the settings file
1178    pub migration_status: MigrationStatus,
1179}
1180
1181#[derive(Debug, Clone, PartialEq, Eq)]
1182pub enum MigrationStatus {
1183    /// No migration was needed - settings are up to date
1184    NotNeeded,
1185    /// Settings were automatically migrated in memory, but the file needs to be updated
1186    Succeeded,
1187    /// Migration was attempted but failed. Original settings were parsed instead.
1188    Failed { error: String },
1189}
1190
1191impl Default for SettingsParseResult {
1192    fn default() -> Self {
1193        Self {
1194            parse_status: ParseStatus::Success,
1195            migration_status: MigrationStatus::NotNeeded,
1196        }
1197    }
1198}
1199
1200impl SettingsParseResult {
1201    pub fn unwrap(self) -> bool {
1202        self.result().unwrap()
1203    }
1204
1205    pub fn expect(self, message: &str) -> bool {
1206        self.result().expect(message)
1207    }
1208
1209    /// Formats the ParseResult as a Result type. This is a lossy conversion
1210    pub fn result(self) -> Result<bool> {
1211        let migration_result = match self.migration_status {
1212            MigrationStatus::NotNeeded => Ok(false),
1213            MigrationStatus::Succeeded => Ok(true),
1214            MigrationStatus::Failed { error } => {
1215                Err(anyhow::format_err!(error)).context("Failed to migrate settings")
1216            }
1217        };
1218
1219        let parse_result = match self.parse_status {
1220            ParseStatus::Success => Ok(()),
1221            ParseStatus::Failed { error } => {
1222                Err(anyhow::format_err!(error)).context("Failed to parse settings")
1223            }
1224        };
1225
1226        match (migration_result, parse_result) {
1227            (migration_result @ Ok(_), Ok(())) => migration_result,
1228            (Err(migration_err), Ok(())) => Err(migration_err),
1229            (_, Err(parse_err)) => Err(parse_err),
1230        }
1231    }
1232
1233    /// Returns true if there were any errors migrating and parsing the settings content or if migration was required but there were no errors
1234    pub fn requires_user_action(&self) -> bool {
1235        matches!(self.parse_status, ParseStatus::Failed { .. })
1236            || matches!(
1237                self.migration_status,
1238                MigrationStatus::Succeeded | MigrationStatus::Failed { .. }
1239            )
1240    }
1241
1242    pub fn ok(self) -> Option<bool> {
1243        self.result().ok()
1244    }
1245
1246    pub fn parse_error(&self) -> Option<String> {
1247        match &self.parse_status {
1248            ParseStatus::Failed { error } => Some(error.clone()),
1249            ParseStatus::Success => None,
1250        }
1251    }
1252}
1253
1254#[derive(Debug, Clone, PartialEq)]
1255pub enum InvalidSettingsError {
1256    LocalSettings {
1257        path: Arc<RelPath>,
1258        message: String,
1259    },
1260    UserSettings {
1261        message: String,
1262    },
1263    ServerSettings {
1264        message: String,
1265    },
1266    DefaultSettings {
1267        message: String,
1268    },
1269    Editorconfig {
1270        path: LocalSettingsPath,
1271        message: String,
1272    },
1273    Tasks {
1274        path: PathBuf,
1275        message: String,
1276    },
1277    Debug {
1278        path: PathBuf,
1279        message: String,
1280    },
1281}
1282
1283impl std::fmt::Display for InvalidSettingsError {
1284    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1285        match self {
1286            InvalidSettingsError::LocalSettings { message, .. }
1287            | InvalidSettingsError::UserSettings { message }
1288            | InvalidSettingsError::ServerSettings { message }
1289            | InvalidSettingsError::DefaultSettings { message }
1290            | InvalidSettingsError::Tasks { message, .. }
1291            | InvalidSettingsError::Editorconfig { message, .. }
1292            | InvalidSettingsError::Debug { message, .. } => {
1293                write!(f, "{message}")
1294            }
1295        }
1296    }
1297}
1298impl std::error::Error for InvalidSettingsError {}
1299
1300impl Debug for SettingsStore {
1301    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1302        f.debug_struct("SettingsStore")
1303            .field(
1304                "types",
1305                &self
1306                    .setting_values
1307                    .values()
1308                    .map(|value| value.setting_type_name())
1309                    .collect::<Vec<_>>(),
1310            )
1311            .field("default_settings", &self.default_settings)
1312            .field("user_settings", &self.user_settings)
1313            .field("local_settings", &self.local_settings)
1314            .finish_non_exhaustive()
1315    }
1316}
1317
1318impl<T: Settings> AnySettingValue for SettingValue<T> {
1319    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any> {
1320        Box::new(T::from_settings(s)) as _
1321    }
1322
1323    fn setting_type_name(&self) -> &'static str {
1324        type_name::<T>()
1325    }
1326
1327    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)> {
1328        self.local_values
1329            .iter()
1330            .map(|(id, path, value)| (*id, path.clone(), value as _))
1331            .collect()
1332    }
1333
1334    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1335        if let Some(SettingsLocation { worktree_id, path }) = path {
1336            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1337                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1338                    return value;
1339                }
1340            }
1341        }
1342
1343        self.global_value
1344            .as_ref()
1345            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1346    }
1347
1348    fn set_global_value(&mut self, value: Box<dyn Any>) {
1349        self.global_value = Some(*value.downcast().unwrap());
1350    }
1351
1352    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<RelPath>, value: Box<dyn Any>) {
1353        let value = *value.downcast().unwrap();
1354        match self
1355            .local_values
1356            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1357        {
1358            Ok(ix) => self.local_values[ix].2 = value,
1359            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1360        }
1361    }
1362
1363    fn clear_local_values(&mut self, root_id: WorktreeId) {
1364        self.local_values
1365            .retain(|(worktree_id, _, _)| *worktree_id != root_id);
1366    }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371    use std::num::NonZeroU32;
1372
1373    use crate::{
1374        ClosePosition, ItemSettingsContent, VsCodeSettingsSource, default_settings,
1375        settings_content::LanguageSettingsContent, test_settings,
1376    };
1377
1378    use super::*;
1379    use unindent::Unindent;
1380    use util::rel_path::rel_path;
1381
1382    #[derive(Debug, PartialEq)]
1383    struct AutoUpdateSetting {
1384        auto_update: bool,
1385    }
1386
1387    impl Settings for AutoUpdateSetting {
1388        fn from_settings(content: &SettingsContent) -> Self {
1389            AutoUpdateSetting {
1390                auto_update: content.auto_update.unwrap(),
1391            }
1392        }
1393    }
1394
1395    #[derive(Debug, PartialEq)]
1396    struct ItemSettings {
1397        close_position: ClosePosition,
1398        git_status: bool,
1399    }
1400
1401    impl Settings for ItemSettings {
1402        fn from_settings(content: &SettingsContent) -> Self {
1403            let content = content.tabs.clone().unwrap();
1404            ItemSettings {
1405                close_position: content.close_position.unwrap(),
1406                git_status: content.git_status.unwrap(),
1407            }
1408        }
1409    }
1410
1411    #[derive(Debug, PartialEq)]
1412    struct DefaultLanguageSettings {
1413        tab_size: NonZeroU32,
1414        preferred_line_length: u32,
1415    }
1416
1417    impl Settings for DefaultLanguageSettings {
1418        fn from_settings(content: &SettingsContent) -> Self {
1419            let content = &content.project.all_languages.defaults;
1420            DefaultLanguageSettings {
1421                tab_size: content.tab_size.unwrap(),
1422                preferred_line_length: content.preferred_line_length.unwrap(),
1423            }
1424        }
1425    }
1426
1427    #[derive(Debug, PartialEq)]
1428    struct ThemeSettings {
1429        buffer_font_family: FontFamilyName,
1430        buffer_font_fallbacks: Vec<FontFamilyName>,
1431    }
1432
1433    impl Settings for ThemeSettings {
1434        fn from_settings(content: &SettingsContent) -> Self {
1435            let content = content.theme.clone();
1436            ThemeSettings {
1437                buffer_font_family: content.buffer_font_family.unwrap(),
1438                buffer_font_fallbacks: content.buffer_font_fallbacks.unwrap(),
1439            }
1440        }
1441    }
1442
1443    #[gpui::test]
1444    fn test_settings_store_basic(cx: &mut App) {
1445        let mut store = SettingsStore::new(cx, &default_settings());
1446        store.register_setting::<AutoUpdateSetting>();
1447        store.register_setting::<ItemSettings>();
1448        store.register_setting::<DefaultLanguageSettings>();
1449
1450        assert_eq!(
1451            store.get::<AutoUpdateSetting>(None),
1452            &AutoUpdateSetting { auto_update: true }
1453        );
1454        assert_eq!(
1455            store.get::<ItemSettings>(None).close_position,
1456            ClosePosition::Right
1457        );
1458
1459        store
1460            .set_user_settings(
1461                r#"{
1462                    "auto_update": false,
1463                    "tabs": {
1464                      "close_position": "left"
1465                    }
1466                }"#,
1467                cx,
1468            )
1469            .unwrap();
1470
1471        assert_eq!(
1472            store.get::<AutoUpdateSetting>(None),
1473            &AutoUpdateSetting { auto_update: false }
1474        );
1475        assert_eq!(
1476            store.get::<ItemSettings>(None).close_position,
1477            ClosePosition::Left
1478        );
1479
1480        store
1481            .set_local_settings(
1482                WorktreeId::from_usize(1),
1483                LocalSettingsPath::InWorktree(rel_path("root1").into()),
1484                LocalSettingsKind::Settings,
1485                Some(r#"{ "tab_size": 5 }"#),
1486                cx,
1487            )
1488            .unwrap();
1489        store
1490            .set_local_settings(
1491                WorktreeId::from_usize(1),
1492                LocalSettingsPath::InWorktree(rel_path("root1/subdir").into()),
1493                LocalSettingsKind::Settings,
1494                Some(r#"{ "preferred_line_length": 50 }"#),
1495                cx,
1496            )
1497            .unwrap();
1498
1499        store
1500            .set_local_settings(
1501                WorktreeId::from_usize(1),
1502                LocalSettingsPath::InWorktree(rel_path("root2").into()),
1503                LocalSettingsKind::Settings,
1504                Some(r#"{ "tab_size": 9, "auto_update": true}"#),
1505                cx,
1506            )
1507            .unwrap();
1508
1509        assert_eq!(
1510            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1511                worktree_id: WorktreeId::from_usize(1),
1512                path: rel_path("root1/something"),
1513            })),
1514            &DefaultLanguageSettings {
1515                preferred_line_length: 80,
1516                tab_size: 5.try_into().unwrap(),
1517            }
1518        );
1519        assert_eq!(
1520            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1521                worktree_id: WorktreeId::from_usize(1),
1522                path: rel_path("root1/subdir/something"),
1523            })),
1524            &DefaultLanguageSettings {
1525                preferred_line_length: 50,
1526                tab_size: 5.try_into().unwrap(),
1527            }
1528        );
1529        assert_eq!(
1530            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1531                worktree_id: WorktreeId::from_usize(1),
1532                path: rel_path("root2/something"),
1533            })),
1534            &DefaultLanguageSettings {
1535                preferred_line_length: 80,
1536                tab_size: 9.try_into().unwrap(),
1537            }
1538        );
1539        assert_eq!(
1540            store.get::<AutoUpdateSetting>(Some(SettingsLocation {
1541                worktree_id: WorktreeId::from_usize(1),
1542                path: rel_path("root2/something")
1543            })),
1544            &AutoUpdateSetting { auto_update: false }
1545        );
1546    }
1547
1548    #[gpui::test]
1549    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1550        let mut store = SettingsStore::new(cx, &test_settings());
1551        store
1552            .set_user_settings(r#"{ "auto_update": false }"#, cx)
1553            .unwrap();
1554        store.register_setting::<AutoUpdateSetting>();
1555
1556        assert_eq!(
1557            store.get::<AutoUpdateSetting>(None),
1558            &AutoUpdateSetting { auto_update: false }
1559        );
1560    }
1561
1562    #[track_caller]
1563    fn check_settings_update(
1564        store: &mut SettingsStore,
1565        old_json: String,
1566        update: fn(&mut SettingsContent),
1567        expected_new_json: String,
1568        cx: &mut App,
1569    ) {
1570        store.set_user_settings(&old_json, cx).ok();
1571        let edits = store.edits_for_update(&old_json, update);
1572        let mut new_json = old_json;
1573        for (range, replacement) in edits.into_iter() {
1574            new_json.replace_range(range, &replacement);
1575        }
1576        pretty_assertions::assert_eq!(new_json, expected_new_json);
1577    }
1578
1579    #[gpui::test]
1580    fn test_setting_store_update(cx: &mut App) {
1581        let mut store = SettingsStore::new(cx, &test_settings());
1582
1583        // entries added and updated
1584        check_settings_update(
1585            &mut store,
1586            r#"{
1587                "languages": {
1588                    "JSON": {
1589                        "auto_indent": true
1590                    }
1591                }
1592            }"#
1593            .unindent(),
1594            |settings| {
1595                settings
1596                    .languages_mut()
1597                    .get_mut("JSON")
1598                    .unwrap()
1599                    .auto_indent = Some(false);
1600
1601                settings.languages_mut().insert(
1602                    "Rust".into(),
1603                    LanguageSettingsContent {
1604                        auto_indent: Some(true),
1605                        ..Default::default()
1606                    },
1607                );
1608            },
1609            r#"{
1610                "languages": {
1611                    "Rust": {
1612                        "auto_indent": true
1613                    },
1614                    "JSON": {
1615                        "auto_indent": false
1616                    }
1617                }
1618            }"#
1619            .unindent(),
1620            cx,
1621        );
1622
1623        // entries removed
1624        check_settings_update(
1625            &mut store,
1626            r#"{
1627                "languages": {
1628                    "Rust": {
1629                        "language_setting_2": true
1630                    },
1631                    "JSON": {
1632                        "language_setting_1": false
1633                    }
1634                }
1635            }"#
1636            .unindent(),
1637            |settings| {
1638                settings.languages_mut().remove("JSON").unwrap();
1639            },
1640            r#"{
1641                "languages": {
1642                    "Rust": {
1643                        "language_setting_2": true
1644                    }
1645                }
1646            }"#
1647            .unindent(),
1648            cx,
1649        );
1650
1651        check_settings_update(
1652            &mut store,
1653            r#"{
1654                "languages": {
1655                    "Rust": {
1656                        "language_setting_2": true
1657                    },
1658                    "JSON": {
1659                        "language_setting_1": false
1660                    }
1661                }
1662            }"#
1663            .unindent(),
1664            |settings| {
1665                settings.languages_mut().remove("Rust").unwrap();
1666            },
1667            r#"{
1668                "languages": {
1669                    "JSON": {
1670                        "language_setting_1": false
1671                    }
1672                }
1673            }"#
1674            .unindent(),
1675            cx,
1676        );
1677
1678        // weird formatting
1679        check_settings_update(
1680            &mut store,
1681            r#"{
1682                "tabs":   { "close_position": "left", "name": "Max"  }
1683                }"#
1684            .unindent(),
1685            |settings| {
1686                settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left);
1687            },
1688            r#"{
1689                "tabs":   { "close_position": "left", "name": "Max"  }
1690                }"#
1691            .unindent(),
1692            cx,
1693        );
1694
1695        // single-line formatting, other keys
1696        check_settings_update(
1697            &mut store,
1698            r#"{ "one": 1, "two": 2 }"#.to_owned(),
1699            |settings| settings.auto_update = Some(true),
1700            r#"{ "auto_update": true, "one": 1, "two": 2 }"#.to_owned(),
1701            cx,
1702        );
1703
1704        // empty object
1705        check_settings_update(
1706            &mut store,
1707            r#"{
1708                "tabs": {}
1709            }"#
1710            .unindent(),
1711            |settings| settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left),
1712            r#"{
1713                "tabs": {
1714                    "close_position": "left"
1715                }
1716            }"#
1717            .unindent(),
1718            cx,
1719        );
1720
1721        // no content
1722        check_settings_update(
1723            &mut store,
1724            r#""#.unindent(),
1725            |settings| {
1726                settings.tabs = Some(ItemSettingsContent {
1727                    git_status: Some(true),
1728                    ..Default::default()
1729                })
1730            },
1731            r#"{
1732              "tabs": {
1733                "git_status": true
1734              }
1735            }
1736            "#
1737            .unindent(),
1738            cx,
1739        );
1740
1741        check_settings_update(
1742            &mut store,
1743            r#"{
1744            }
1745            "#
1746            .unindent(),
1747            |settings| settings.title_bar.get_or_insert_default().show_branch_name = Some(true),
1748            r#"{
1749              "title_bar": {
1750                "show_branch_name": true
1751              }
1752            }
1753            "#
1754            .unindent(),
1755            cx,
1756        );
1757    }
1758
1759    #[gpui::test]
1760    fn test_vscode_import(cx: &mut App) {
1761        let mut store = SettingsStore::new(cx, &test_settings());
1762        store.register_setting::<DefaultLanguageSettings>();
1763        store.register_setting::<ItemSettings>();
1764        store.register_setting::<AutoUpdateSetting>();
1765        store.register_setting::<ThemeSettings>();
1766
1767        // create settings that werent present
1768        check_vscode_import(
1769            &mut store,
1770            r#"{
1771            }
1772            "#
1773            .unindent(),
1774            r#" { "editor.tabSize": 37 } "#.to_owned(),
1775            r#"{
1776              "base_keymap": "VSCode",
1777              "tab_size": 37
1778            }
1779            "#
1780            .unindent(),
1781            cx,
1782        );
1783
1784        // persist settings that were present
1785        check_vscode_import(
1786            &mut store,
1787            r#"{
1788                "preferred_line_length": 99,
1789            }
1790            "#
1791            .unindent(),
1792            r#"{ "editor.tabSize": 42 }"#.to_owned(),
1793            r#"{
1794                "base_keymap": "VSCode",
1795                "tab_size": 42,
1796                "preferred_line_length": 99,
1797            }
1798            "#
1799            .unindent(),
1800            cx,
1801        );
1802
1803        // don't clobber settings that aren't present in vscode
1804        check_vscode_import(
1805            &mut store,
1806            r#"{
1807                "preferred_line_length": 99,
1808                "tab_size": 42
1809            }
1810            "#
1811            .unindent(),
1812            r#"{}"#.to_owned(),
1813            r#"{
1814                "base_keymap": "VSCode",
1815                "preferred_line_length": 99,
1816                "tab_size": 42
1817            }
1818            "#
1819            .unindent(),
1820            cx,
1821        );
1822
1823        // custom enum
1824        check_vscode_import(
1825            &mut store,
1826            r#"{
1827            }
1828            "#
1829            .unindent(),
1830            r#"{ "git.decorations.enabled": true }"#.to_owned(),
1831            r#"{
1832              "project_panel": {
1833                "git_status": true
1834              },
1835              "outline_panel": {
1836                "git_status": true
1837              },
1838              "base_keymap": "VSCode",
1839              "tabs": {
1840                "git_status": true
1841              }
1842            }
1843            "#
1844            .unindent(),
1845            cx,
1846        );
1847
1848        // font-family
1849        check_vscode_import(
1850            &mut store,
1851            r#"{
1852            }
1853            "#
1854            .unindent(),
1855            r#"{ "editor.fontFamily": "Cascadia Code, 'Consolas', Courier New" }"#.to_owned(),
1856            r#"{
1857              "base_keymap": "VSCode",
1858              "buffer_font_fallbacks": [
1859                "Consolas",
1860                "Courier New"
1861              ],
1862              "buffer_font_family": "Cascadia Code"
1863            }
1864            "#
1865            .unindent(),
1866            cx,
1867        );
1868    }
1869
1870    #[track_caller]
1871    fn check_vscode_import(
1872        store: &mut SettingsStore,
1873        old: String,
1874        vscode: String,
1875        expected: String,
1876        cx: &mut App,
1877    ) {
1878        store.set_user_settings(&old, cx).ok();
1879        let new = store.get_vscode_edits(
1880            old,
1881            &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
1882        );
1883        pretty_assertions::assert_eq!(new, expected);
1884    }
1885
1886    #[gpui::test]
1887    fn test_update_git_settings(cx: &mut App) {
1888        let store = SettingsStore::new(cx, &test_settings());
1889
1890        let actual = store.new_text_for_update("{}".to_string(), |current| {
1891            current
1892                .git
1893                .get_or_insert_default()
1894                .inline_blame
1895                .get_or_insert_default()
1896                .enabled = Some(true);
1897        });
1898        pretty_assertions::assert_str_eq!(
1899            actual,
1900            r#"{
1901              "git": {
1902                "inline_blame": {
1903                  "enabled": true
1904                }
1905              }
1906            }
1907            "#
1908            .unindent()
1909        );
1910    }
1911
1912    #[gpui::test]
1913    fn test_global_settings(cx: &mut App) {
1914        let mut store = SettingsStore::new(cx, &test_settings());
1915        store.register_setting::<ItemSettings>();
1916
1917        // Set global settings - these should override defaults but not user settings
1918        store
1919            .set_global_settings(
1920                r#"{
1921                    "tabs": {
1922                        "close_position": "right",
1923                        "git_status": true,
1924                    }
1925                }"#,
1926                cx,
1927            )
1928            .unwrap();
1929
1930        // Before user settings, global settings should apply
1931        assert_eq!(
1932            store.get::<ItemSettings>(None),
1933            &ItemSettings {
1934                close_position: ClosePosition::Right,
1935                git_status: true,
1936            }
1937        );
1938
1939        // Set user settings - these should override both defaults and global
1940        store
1941            .set_user_settings(
1942                r#"{
1943                    "tabs": {
1944                        "close_position": "left"
1945                    }
1946                }"#,
1947                cx,
1948            )
1949            .unwrap();
1950
1951        // User settings should override global settings
1952        assert_eq!(
1953            store.get::<ItemSettings>(None),
1954            &ItemSettings {
1955                close_position: ClosePosition::Left,
1956                git_status: true, // Staff from global settings
1957            }
1958        );
1959    }
1960
1961    #[gpui::test]
1962    fn test_get_value_for_field_basic(cx: &mut App) {
1963        let mut store = SettingsStore::new(cx, &test_settings());
1964        store.register_setting::<DefaultLanguageSettings>();
1965
1966        store
1967            .set_user_settings(r#"{"preferred_line_length": 0}"#, cx)
1968            .unwrap();
1969        let local = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
1970        store
1971            .set_local_settings(
1972                local.0,
1973                LocalSettingsPath::InWorktree(local.1.clone()),
1974                LocalSettingsKind::Settings,
1975                Some(r#"{}"#),
1976                cx,
1977            )
1978            .unwrap();
1979
1980        fn get(content: &SettingsContent) -> Option<&u32> {
1981            content
1982                .project
1983                .all_languages
1984                .defaults
1985                .preferred_line_length
1986                .as_ref()
1987        }
1988
1989        let default_value = *get(&store.default_settings).unwrap();
1990
1991        assert_eq!(
1992            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1993            (SettingsFile::User, Some(&0))
1994        );
1995        assert_eq!(
1996            store.get_value_from_file(SettingsFile::User, get),
1997            (SettingsFile::User, Some(&0))
1998        );
1999        store.set_user_settings(r#"{}"#, cx).unwrap();
2000        assert_eq!(
2001            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
2002            (SettingsFile::Default, Some(&default_value))
2003        );
2004        store
2005            .set_local_settings(
2006                local.0,
2007                LocalSettingsPath::InWorktree(local.1.clone()),
2008                LocalSettingsKind::Settings,
2009                Some(r#"{"preferred_line_length": 80}"#),
2010                cx,
2011            )
2012            .unwrap();
2013        assert_eq!(
2014            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
2015            (SettingsFile::Project(local), Some(&80))
2016        );
2017        assert_eq!(
2018            store.get_value_from_file(SettingsFile::User, get),
2019            (SettingsFile::Default, Some(&default_value))
2020        );
2021    }
2022
2023    #[gpui::test]
2024    fn test_get_value_for_field_local_worktrees_dont_interfere(cx: &mut App) {
2025        let mut store = SettingsStore::new(cx, &test_settings());
2026        store.register_setting::<DefaultLanguageSettings>();
2027        store.register_setting::<AutoUpdateSetting>();
2028
2029        let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
2030
2031        let local_1_child = (
2032            WorktreeId::from_usize(0),
2033            RelPath::new(
2034                std::path::Path::new("child1"),
2035                util::paths::PathStyle::Posix,
2036            )
2037            .unwrap()
2038            .into_arc(),
2039        );
2040
2041        let local_2 = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
2042        let local_2_child = (
2043            WorktreeId::from_usize(1),
2044            RelPath::new(
2045                std::path::Path::new("child2"),
2046                util::paths::PathStyle::Posix,
2047            )
2048            .unwrap()
2049            .into_arc(),
2050        );
2051
2052        fn get(content: &SettingsContent) -> Option<&u32> {
2053            content
2054                .project
2055                .all_languages
2056                .defaults
2057                .preferred_line_length
2058                .as_ref()
2059        }
2060
2061        store
2062            .set_local_settings(
2063                local_1.0,
2064                LocalSettingsPath::InWorktree(local_1.1.clone()),
2065                LocalSettingsKind::Settings,
2066                Some(r#"{"preferred_line_length": 1}"#),
2067                cx,
2068            )
2069            .unwrap();
2070        store
2071            .set_local_settings(
2072                local_1_child.0,
2073                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2074                LocalSettingsKind::Settings,
2075                Some(r#"{}"#),
2076                cx,
2077            )
2078            .unwrap();
2079        store
2080            .set_local_settings(
2081                local_2.0,
2082                LocalSettingsPath::InWorktree(local_2.1.clone()),
2083                LocalSettingsKind::Settings,
2084                Some(r#"{"preferred_line_length": 2}"#),
2085                cx,
2086            )
2087            .unwrap();
2088        store
2089            .set_local_settings(
2090                local_2_child.0,
2091                LocalSettingsPath::InWorktree(local_2_child.1.clone()),
2092                LocalSettingsKind::Settings,
2093                Some(r#"{}"#),
2094                cx,
2095            )
2096            .unwrap();
2097
2098        // each local child should only inherit from it's parent
2099        assert_eq!(
2100            store.get_value_from_file(SettingsFile::Project(local_2_child), get),
2101            (SettingsFile::Project(local_2), Some(&2))
2102        );
2103        assert_eq!(
2104            store.get_value_from_file(SettingsFile::Project(local_1_child.clone()), get),
2105            (SettingsFile::Project(local_1.clone()), Some(&1))
2106        );
2107
2108        // adjacent children should be treated as siblings not inherit from each other
2109        let local_1_adjacent_child = (local_1.0, rel_path("adjacent_child").into_arc());
2110        store
2111            .set_local_settings(
2112                local_1_adjacent_child.0,
2113                LocalSettingsPath::InWorktree(local_1_adjacent_child.1.clone()),
2114                LocalSettingsKind::Settings,
2115                Some(r#"{}"#),
2116                cx,
2117            )
2118            .unwrap();
2119        store
2120            .set_local_settings(
2121                local_1_child.0,
2122                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2123                LocalSettingsKind::Settings,
2124                Some(r#"{"preferred_line_length": 3}"#),
2125                cx,
2126            )
2127            .unwrap();
2128
2129        assert_eq!(
2130            store.get_value_from_file(SettingsFile::Project(local_1_adjacent_child.clone()), get),
2131            (SettingsFile::Project(local_1.clone()), Some(&1))
2132        );
2133        store
2134            .set_local_settings(
2135                local_1_adjacent_child.0,
2136                LocalSettingsPath::InWorktree(local_1_adjacent_child.1),
2137                LocalSettingsKind::Settings,
2138                Some(r#"{"preferred_line_length": 3}"#),
2139                cx,
2140            )
2141            .unwrap();
2142        store
2143            .set_local_settings(
2144                local_1_child.0,
2145                LocalSettingsPath::InWorktree(local_1_child.1.clone()),
2146                LocalSettingsKind::Settings,
2147                Some(r#"{}"#),
2148                cx,
2149            )
2150            .unwrap();
2151        assert_eq!(
2152            store.get_value_from_file(SettingsFile::Project(local_1_child), get),
2153            (SettingsFile::Project(local_1), Some(&1))
2154        );
2155    }
2156
2157    #[gpui::test]
2158    fn test_get_overrides_for_field(cx: &mut App) {
2159        let mut store = SettingsStore::new(cx, &test_settings());
2160        store.register_setting::<DefaultLanguageSettings>();
2161
2162        let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
2163        let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc());
2164        let wt0_child2 = (WorktreeId::from_usize(0), rel_path("child2").into_arc());
2165
2166        let wt1_root = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
2167        let wt1_subdir = (WorktreeId::from_usize(1), rel_path("subdir").into_arc());
2168
2169        fn get(content: &SettingsContent) -> &Option<u32> {
2170            &content.project.all_languages.defaults.preferred_line_length
2171        }
2172
2173        store
2174            .set_user_settings(r#"{"preferred_line_length": 100}"#, cx)
2175            .unwrap();
2176
2177        store
2178            .set_local_settings(
2179                wt0_root.0,
2180                LocalSettingsPath::InWorktree(wt0_root.1.clone()),
2181                LocalSettingsKind::Settings,
2182                Some(r#"{"preferred_line_length": 80}"#),
2183                cx,
2184            )
2185            .unwrap();
2186        store
2187            .set_local_settings(
2188                wt0_child1.0,
2189                LocalSettingsPath::InWorktree(wt0_child1.1.clone()),
2190                LocalSettingsKind::Settings,
2191                Some(r#"{"preferred_line_length": 120}"#),
2192                cx,
2193            )
2194            .unwrap();
2195        store
2196            .set_local_settings(
2197                wt0_child2.0,
2198                LocalSettingsPath::InWorktree(wt0_child2.1.clone()),
2199                LocalSettingsKind::Settings,
2200                Some(r#"{}"#),
2201                cx,
2202            )
2203            .unwrap();
2204
2205        store
2206            .set_local_settings(
2207                wt1_root.0,
2208                LocalSettingsPath::InWorktree(wt1_root.1.clone()),
2209                LocalSettingsKind::Settings,
2210                Some(r#"{"preferred_line_length": 90}"#),
2211                cx,
2212            )
2213            .unwrap();
2214        store
2215            .set_local_settings(
2216                wt1_subdir.0,
2217                LocalSettingsPath::InWorktree(wt1_subdir.1.clone()),
2218                LocalSettingsKind::Settings,
2219                Some(r#"{}"#),
2220                cx,
2221            )
2222            .unwrap();
2223
2224        let overrides = store.get_overrides_for_field(SettingsFile::Default, get);
2225        assert_eq!(
2226            overrides,
2227            vec![
2228                SettingsFile::User,
2229                SettingsFile::Project(wt0_root.clone()),
2230                SettingsFile::Project(wt0_child1.clone()),
2231                SettingsFile::Project(wt1_root.clone()),
2232            ]
2233        );
2234
2235        let overrides = store.get_overrides_for_field(SettingsFile::User, get);
2236        assert_eq!(
2237            overrides,
2238            vec![
2239                SettingsFile::Project(wt0_root.clone()),
2240                SettingsFile::Project(wt0_child1.clone()),
2241                SettingsFile::Project(wt1_root.clone()),
2242            ]
2243        );
2244
2245        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_root), get);
2246        assert_eq!(overrides, vec![]);
2247
2248        let overrides =
2249            store.get_overrides_for_field(SettingsFile::Project(wt0_child1.clone()), get);
2250        assert_eq!(overrides, vec![]);
2251
2252        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child2), get);
2253        assert_eq!(overrides, vec![]);
2254
2255        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_root), get);
2256        assert_eq!(overrides, vec![]);
2257
2258        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_subdir), get);
2259        assert_eq!(overrides, vec![]);
2260
2261        let wt0_deep_child = (
2262            WorktreeId::from_usize(0),
2263            rel_path("child1/subdir").into_arc(),
2264        );
2265        store
2266            .set_local_settings(
2267                wt0_deep_child.0,
2268                LocalSettingsPath::InWorktree(wt0_deep_child.1.clone()),
2269                LocalSettingsKind::Settings,
2270                Some(r#"{"preferred_line_length": 140}"#),
2271                cx,
2272            )
2273            .unwrap();
2274
2275        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_deep_child), get);
2276        assert_eq!(overrides, vec![]);
2277
2278        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child1), get);
2279        assert_eq!(overrides, vec![]);
2280    }
2281
2282    #[test]
2283    fn test_file_ord() {
2284        let wt0_root =
2285            SettingsFile::Project((WorktreeId::from_usize(0), RelPath::empty().into_arc()));
2286        let wt0_child1 =
2287            SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child1").into_arc()));
2288        let wt0_child2 =
2289            SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child2").into_arc()));
2290
2291        let wt1_root =
2292            SettingsFile::Project((WorktreeId::from_usize(1), RelPath::empty().into_arc()));
2293        let wt1_subdir =
2294            SettingsFile::Project((WorktreeId::from_usize(1), rel_path("subdir").into_arc()));
2295
2296        let mut files = vec![
2297            &wt1_root,
2298            &SettingsFile::Default,
2299            &wt0_root,
2300            &wt1_subdir,
2301            &wt0_child2,
2302            &SettingsFile::Server,
2303            &wt0_child1,
2304            &SettingsFile::User,
2305        ];
2306
2307        files.sort();
2308        pretty_assertions::assert_eq!(
2309            files,
2310            vec![
2311                &wt0_child2,
2312                &wt0_child1,
2313                &wt0_root,
2314                &wt1_subdir,
2315                &wt1_root,
2316                &SettingsFile::Server,
2317                &SettingsFile::User,
2318                &SettingsFile::Default,
2319            ]
2320        )
2321    }
2322
2323    #[gpui::test]
2324    fn test_lsp_settings_schema_generation(cx: &mut App) {
2325        let store = SettingsStore::test(cx);
2326
2327        let schema = store.json_schema(&SettingsJsonSchemaParams {
2328            language_names: &["Rust".to_string(), "TypeScript".to_string()],
2329            font_names: &["Zed Mono".to_string()],
2330            theme_names: &["One Dark".into()],
2331            icon_theme_names: &["Zed Icons".into()],
2332            lsp_adapter_names: &[
2333                "rust-analyzer".to_string(),
2334                "typescript-language-server".to_string(),
2335            ],
2336        });
2337
2338        let properties = schema
2339            .pointer("/$defs/LspSettingsMap/properties")
2340            .expect("LspSettingsMap should have properties")
2341            .as_object()
2342            .unwrap();
2343
2344        assert!(properties.contains_key("rust-analyzer"));
2345        assert!(properties.contains_key("typescript-language-server"));
2346
2347        let init_options_ref = properties
2348            .get("rust-analyzer")
2349            .unwrap()
2350            .pointer("/properties/initialization_options/$ref")
2351            .expect("initialization_options should have a $ref")
2352            .as_str()
2353            .unwrap();
2354
2355        assert_eq!(init_options_ref, "zed://schemas/settings/lsp/rust-analyzer");
2356    }
2357
2358    #[gpui::test]
2359    fn test_lsp_project_settings_schema_generation(cx: &mut App) {
2360        let store = SettingsStore::test(cx);
2361
2362        let schema = store.project_json_schema(&SettingsJsonSchemaParams {
2363            language_names: &["Rust".to_string(), "TypeScript".to_string()],
2364            font_names: &["Zed Mono".to_string()],
2365            theme_names: &["One Dark".into()],
2366            icon_theme_names: &["Zed Icons".into()],
2367            lsp_adapter_names: &[
2368                "rust-analyzer".to_string(),
2369                "typescript-language-server".to_string(),
2370            ],
2371        });
2372
2373        let properties = schema
2374            .pointer("/$defs/LspSettingsMap/properties")
2375            .expect("LspSettingsMap should have properties")
2376            .as_object()
2377            .unwrap();
2378
2379        assert!(properties.contains_key("rust-analyzer"));
2380        assert!(properties.contains_key("typescript-language-server"));
2381
2382        let init_options_ref = properties
2383            .get("rust-analyzer")
2384            .unwrap()
2385            .pointer("/properties/initialization_options/$ref")
2386            .expect("initialization_options should have a $ref")
2387            .as_str()
2388            .unwrap();
2389
2390        assert_eq!(init_options_ref, "zed://schemas/settings/lsp/rust-analyzer");
2391    }
2392
2393    #[gpui::test]
2394    fn test_project_json_schema_differs_from_user_schema(cx: &mut App) {
2395        let store = SettingsStore::test(cx);
2396
2397        let params = SettingsJsonSchemaParams {
2398            language_names: &["Rust".to_string()],
2399            font_names: &["Zed Mono".to_string()],
2400            theme_names: &["One Dark".into()],
2401            icon_theme_names: &["Zed Icons".into()],
2402            lsp_adapter_names: &["rust-analyzer".to_string()],
2403        };
2404
2405        let user_schema = store.json_schema(&params);
2406        let project_schema = store.project_json_schema(&params);
2407
2408        assert_ne!(user_schema, project_schema);
2409
2410        let user_schema_str = serde_json::to_string(&user_schema).unwrap();
2411        let project_schema_str = serde_json::to_string(&project_schema).unwrap();
2412
2413        assert!(user_schema_str.contains("\"auto_update\""));
2414        assert!(!project_schema_str.contains("\"auto_update\""));
2415    }
2416}