settings_store.rs

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