settings_store.rs

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