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