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