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