settings_store.rs

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