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