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