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