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) -> 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    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        // todo(settings_ui): Add a metadata field for overriding the "overrides" tag, for contextually different settings
 543        //  e.g. disable AI isn't overridden, or a vec that gets extended instead or some such
 544
 545        // todo(settings_ui) cache all files
 546        let all_files = self.get_all_files();
 547        let mut found_file = false;
 548
 549        for file in all_files.into_iter() {
 550            if !found_file && file != target_file && file != SettingsFile::Default {
 551                continue;
 552            }
 553            found_file = true;
 554
 555            if let SettingsFile::Project((worktree_id, ref path)) = file
 556                && let SettingsFile::Project((target_worktree_id, ref target_path)) = target_file
 557                && (worktree_id != target_worktree_id || !target_path.starts_with(&path))
 558            {
 559                // if requesting value from a local file, don't return values from local files in different worktrees
 560                continue;
 561            }
 562
 563            let Some(content) = self.get_content_for_file(file.clone()) else {
 564                continue;
 565            };
 566            if let Some(value) = pick(content).as_ref() {
 567                return (file, Some(value));
 568            }
 569        }
 570
 571        (SettingsFile::Default, None)
 572    }
 573}
 574
 575impl SettingsStore {
 576    /// Updates the value of a setting in a JSON file, returning the new text
 577    /// for that JSON file.
 578    pub fn new_text_for_update(
 579        &self,
 580        old_text: String,
 581        update: impl FnOnce(&mut SettingsContent),
 582    ) -> String {
 583        let edits = self.edits_for_update(&old_text, update);
 584        let mut new_text = old_text;
 585        for (range, replacement) in edits.into_iter() {
 586            new_text.replace_range(range, &replacement);
 587        }
 588        new_text
 589    }
 590
 591    pub fn get_vscode_edits(&self, old_text: String, vscode: &VsCodeSettings) -> String {
 592        self.new_text_for_update(old_text, |settings_content| {
 593            for v in self.setting_values.values() {
 594                v.import_from_vscode(vscode, settings_content)
 595            }
 596        })
 597    }
 598
 599    /// Updates the value of a setting in a JSON file, returning a list
 600    /// of edits to apply to the JSON file.
 601    pub fn edits_for_update(
 602        &self,
 603        text: &str,
 604        update: impl FnOnce(&mut SettingsContent),
 605    ) -> Vec<(Range<usize>, String)> {
 606        let old_content: UserSettingsContent =
 607            parse_json_with_comments(text).log_err().unwrap_or_default();
 608        let mut new_content = old_content.clone();
 609        update(&mut new_content.content);
 610
 611        let old_value = serde_json::to_value(&old_content).unwrap();
 612        let new_value = serde_json::to_value(new_content).unwrap();
 613
 614        let mut key_path = Vec::new();
 615        let mut edits = Vec::new();
 616        let tab_size = self.json_tab_size();
 617        let mut text = text.to_string();
 618        update_value_in_json_text(
 619            &mut text,
 620            &mut key_path,
 621            tab_size,
 622            &old_value,
 623            &new_value,
 624            &mut edits,
 625        );
 626        edits
 627    }
 628
 629    pub fn json_tab_size(&self) -> usize {
 630        2
 631    }
 632
 633    /// Sets the default settings via a JSON string.
 634    ///
 635    /// The string should contain a JSON object with a default value for every setting.
 636    pub fn set_default_settings(
 637        &mut self,
 638        default_settings_content: &str,
 639        cx: &mut App,
 640    ) -> Result<()> {
 641        self.default_settings = parse_json_with_comments(default_settings_content)?;
 642        self.recompute_values(None, cx)?;
 643        Ok(())
 644    }
 645
 646    /// Sets the user settings via a JSON string.
 647    pub fn set_user_settings(&mut self, user_settings_content: &str, cx: &mut App) -> Result<()> {
 648        let settings: UserSettingsContent = if user_settings_content.is_empty() {
 649            parse_json_with_comments("{}")?
 650        } else {
 651            parse_json_with_comments(user_settings_content)?
 652        };
 653
 654        self.user_settings = Some(settings);
 655        self.recompute_values(None, cx)?;
 656        Ok(())
 657    }
 658
 659    /// Sets the global settings via a JSON string.
 660    pub fn set_global_settings(
 661        &mut self,
 662        global_settings_content: &str,
 663        cx: &mut App,
 664    ) -> Result<()> {
 665        let settings: SettingsContent = if global_settings_content.is_empty() {
 666            parse_json_with_comments("{}")?
 667        } else {
 668            parse_json_with_comments(global_settings_content)?
 669        };
 670
 671        self.global_settings = Some(Box::new(settings));
 672        self.recompute_values(None, cx)?;
 673        Ok(())
 674    }
 675
 676    pub fn set_server_settings(
 677        &mut self,
 678        server_settings_content: &str,
 679        cx: &mut App,
 680    ) -> Result<()> {
 681        let settings: Option<SettingsContent> = if server_settings_content.is_empty() {
 682            None
 683        } else {
 684            parse_json_with_comments(server_settings_content)?
 685        };
 686
 687        // Rewrite the server settings into a content type
 688        self.server_settings = settings.map(|settings| Box::new(settings));
 689
 690        self.recompute_values(None, cx)?;
 691        Ok(())
 692    }
 693
 694    /// Add or remove a set of local settings via a JSON string.
 695    pub fn set_local_settings(
 696        &mut self,
 697        root_id: WorktreeId,
 698        directory_path: Arc<RelPath>,
 699        kind: LocalSettingsKind,
 700        settings_content: Option<&str>,
 701        cx: &mut App,
 702    ) -> std::result::Result<(), InvalidSettingsError> {
 703        let mut zed_settings_changed = false;
 704        match (
 705            kind,
 706            settings_content
 707                .map(|content| content.trim())
 708                .filter(|content| !content.is_empty()),
 709        ) {
 710            (LocalSettingsKind::Tasks, _) => {
 711                return Err(InvalidSettingsError::Tasks {
 712                    message: "Attempted to submit tasks into the settings store".to_string(),
 713                    path: directory_path
 714                        .join(RelPath::unix(task_file_name()).unwrap())
 715                        .as_std_path()
 716                        .to_path_buf(),
 717                });
 718            }
 719            (LocalSettingsKind::Debug, _) => {
 720                return Err(InvalidSettingsError::Debug {
 721                    message: "Attempted to submit debugger config into the settings store"
 722                        .to_string(),
 723                    path: directory_path
 724                        .join(RelPath::unix(task_file_name()).unwrap())
 725                        .as_std_path()
 726                        .to_path_buf(),
 727                });
 728            }
 729            (LocalSettingsKind::Settings, None) => {
 730                zed_settings_changed = self
 731                    .local_settings
 732                    .remove(&(root_id, directory_path.clone()))
 733                    .is_some()
 734            }
 735            (LocalSettingsKind::Editorconfig, None) => {
 736                self.raw_editorconfig_settings
 737                    .remove(&(root_id, directory_path.clone()));
 738            }
 739            (LocalSettingsKind::Settings, Some(settings_contents)) => {
 740                let new_settings = parse_json_with_comments::<ProjectSettingsContent>(
 741                    settings_contents,
 742                )
 743                .map_err(|e| InvalidSettingsError::LocalSettings {
 744                    path: directory_path.join(local_settings_file_relative_path()),
 745                    message: e.to_string(),
 746                })?;
 747                match self.local_settings.entry((root_id, directory_path.clone())) {
 748                    btree_map::Entry::Vacant(v) => {
 749                        v.insert(SettingsContent {
 750                            project: new_settings,
 751                            ..Default::default()
 752                        });
 753                        zed_settings_changed = true;
 754                    }
 755                    btree_map::Entry::Occupied(mut o) => {
 756                        if &o.get().project != &new_settings {
 757                            o.insert(SettingsContent {
 758                                project: new_settings,
 759                                ..Default::default()
 760                            });
 761                            zed_settings_changed = true;
 762                        }
 763                    }
 764                }
 765            }
 766            (LocalSettingsKind::Editorconfig, Some(editorconfig_contents)) => {
 767                match self
 768                    .raw_editorconfig_settings
 769                    .entry((root_id, directory_path.clone()))
 770                {
 771                    btree_map::Entry::Vacant(v) => match editorconfig_contents.parse() {
 772                        Ok(new_contents) => {
 773                            v.insert((editorconfig_contents.to_owned(), Some(new_contents)));
 774                        }
 775                        Err(e) => {
 776                            v.insert((editorconfig_contents.to_owned(), None));
 777                            return Err(InvalidSettingsError::Editorconfig {
 778                                message: e.to_string(),
 779                                path: directory_path
 780                                    .join(RelPath::unix(EDITORCONFIG_NAME).unwrap()),
 781                            });
 782                        }
 783                    },
 784                    btree_map::Entry::Occupied(mut o) => {
 785                        if o.get().0 != editorconfig_contents {
 786                            match editorconfig_contents.parse() {
 787                                Ok(new_contents) => {
 788                                    o.insert((
 789                                        editorconfig_contents.to_owned(),
 790                                        Some(new_contents),
 791                                    ));
 792                                }
 793                                Err(e) => {
 794                                    o.insert((editorconfig_contents.to_owned(), None));
 795                                    return Err(InvalidSettingsError::Editorconfig {
 796                                        message: e.to_string(),
 797                                        path: directory_path
 798                                            .join(RelPath::unix(EDITORCONFIG_NAME).unwrap()),
 799                                    });
 800                                }
 801                            }
 802                        }
 803                    }
 804                }
 805            }
 806        };
 807
 808        if zed_settings_changed {
 809            self.recompute_values(Some((root_id, &directory_path)), cx)?;
 810        }
 811        Ok(())
 812    }
 813
 814    pub fn set_extension_settings(
 815        &mut self,
 816        content: ExtensionsSettingsContent,
 817        cx: &mut App,
 818    ) -> Result<()> {
 819        self.extension_settings = Some(Box::new(SettingsContent {
 820            project: ProjectSettingsContent {
 821                all_languages: content.all_languages,
 822                ..Default::default()
 823            },
 824            ..Default::default()
 825        }));
 826        self.recompute_values(None, cx)?;
 827        Ok(())
 828    }
 829
 830    /// Add or remove a set of local settings via a JSON string.
 831    pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut App) -> Result<()> {
 832        self.local_settings
 833            .retain(|(worktree_id, _), _| worktree_id != &root_id);
 834        self.recompute_values(Some((root_id, RelPath::empty())), cx)?;
 835        Ok(())
 836    }
 837
 838    pub fn local_settings(
 839        &self,
 840        root_id: WorktreeId,
 841    ) -> impl '_ + Iterator<Item = (Arc<RelPath>, &ProjectSettingsContent)> {
 842        self.local_settings
 843            .range(
 844                (root_id, RelPath::empty().into())
 845                    ..(
 846                        WorktreeId::from_usize(root_id.to_usize() + 1),
 847                        RelPath::empty().into(),
 848                    ),
 849            )
 850            .map(|((_, path), content)| (path.clone(), &content.project))
 851    }
 852
 853    pub fn local_editorconfig_settings(
 854        &self,
 855        root_id: WorktreeId,
 856    ) -> impl '_ + Iterator<Item = (Arc<RelPath>, String, Option<Editorconfig>)> {
 857        self.raw_editorconfig_settings
 858            .range(
 859                (root_id, RelPath::empty().into())
 860                    ..(
 861                        WorktreeId::from_usize(root_id.to_usize() + 1),
 862                        RelPath::empty().into(),
 863                    ),
 864            )
 865            .map(|((_, path), (content, parsed_content))| {
 866                (path.clone(), content.clone(), parsed_content.clone())
 867            })
 868    }
 869
 870    pub fn json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
 871        let mut generator = schemars::generate::SchemaSettings::draft2019_09()
 872            .with_transform(DefaultDenyUnknownFields)
 873            .into_generator();
 874
 875        UserSettingsContent::json_schema(&mut generator);
 876
 877        let language_settings_content_ref = generator
 878            .subschema_for::<LanguageSettingsContent>()
 879            .to_value();
 880
 881        replace_subschema::<LanguageToSettingsMap>(&mut generator, || {
 882            json_schema!({
 883                "type": "object",
 884                "properties": params
 885                    .language_names
 886                    .iter()
 887                    .map(|name| {
 888                        (
 889                            name.clone(),
 890                            language_settings_content_ref.clone(),
 891                        )
 892                    })
 893                    .collect::<serde_json::Map<_, _>>(),
 894                "errorMessage": "No language with this name is installed."
 895            })
 896        });
 897
 898        replace_subschema::<FontFamilyName>(&mut generator, || {
 899            json_schema!({
 900                "type": "string",
 901                "enum": params.font_names,
 902            })
 903        });
 904
 905        replace_subschema::<ThemeName>(&mut generator, || {
 906            json_schema!({
 907                "type": "string",
 908                "enum": params.theme_names,
 909            })
 910        });
 911
 912        replace_subschema::<IconThemeName>(&mut generator, || {
 913            json_schema!({
 914                "type": "string",
 915                "enum": params.icon_theme_names,
 916            })
 917        });
 918
 919        generator
 920            .root_schema_for::<UserSettingsContent>()
 921            .to_value()
 922    }
 923
 924    fn recompute_values(
 925        &mut self,
 926        changed_local_path: Option<(WorktreeId, &RelPath)>,
 927        cx: &mut App,
 928    ) -> std::result::Result<(), InvalidSettingsError> {
 929        // Reload the global and local values for every setting.
 930        let mut project_settings_stack = Vec::<SettingsContent>::new();
 931        let mut paths_stack = Vec::<Option<(WorktreeId, &RelPath)>>::new();
 932
 933        if changed_local_path.is_none() {
 934            let mut merged = self.default_settings.as_ref().clone();
 935            merged.merge_from_option(self.extension_settings.as_deref());
 936            merged.merge_from_option(self.global_settings.as_deref());
 937            if let Some(user_settings) = self.user_settings.as_ref() {
 938                merged.merge_from(&user_settings.content);
 939                merged.merge_from_option(user_settings.for_release_channel());
 940                merged.merge_from_option(user_settings.for_os());
 941                merged.merge_from_option(user_settings.for_profile(cx));
 942            }
 943            merged.merge_from_option(self.server_settings.as_deref());
 944            self.merged_settings = Rc::new(merged);
 945
 946            for setting_value in self.setting_values.values_mut() {
 947                let value = setting_value.from_settings(&self.merged_settings);
 948                setting_value.set_global_value(value);
 949            }
 950        }
 951
 952        for ((root_id, directory_path), local_settings) in &self.local_settings {
 953            // Build a stack of all of the local values for that setting.
 954            while let Some(prev_entry) = paths_stack.last() {
 955                if let Some((prev_root_id, prev_path)) = prev_entry
 956                    && (root_id != prev_root_id || !directory_path.starts_with(prev_path))
 957                {
 958                    paths_stack.pop();
 959                    project_settings_stack.pop();
 960                    continue;
 961                }
 962                break;
 963            }
 964
 965            paths_stack.push(Some((*root_id, directory_path.as_ref())));
 966            let mut merged_local_settings = if let Some(deepest) = project_settings_stack.last() {
 967                (*deepest).clone()
 968            } else {
 969                self.merged_settings.as_ref().clone()
 970            };
 971            merged_local_settings.merge_from(local_settings);
 972
 973            project_settings_stack.push(merged_local_settings);
 974
 975            // If a local settings file changed, then avoid recomputing local
 976            // settings for any path outside of that directory.
 977            if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
 978                *root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
 979            }) {
 980                continue;
 981            }
 982
 983            for setting_value in self.setting_values.values_mut() {
 984                let value = setting_value.from_settings(&project_settings_stack.last().unwrap());
 985                setting_value.set_local_value(*root_id, directory_path.clone(), value);
 986            }
 987        }
 988        Ok(())
 989    }
 990
 991    pub fn editorconfig_properties(
 992        &self,
 993        for_worktree: WorktreeId,
 994        for_path: &RelPath,
 995    ) -> Option<EditorconfigProperties> {
 996        let mut properties = EditorconfigProperties::new();
 997
 998        for (directory_with_config, _, parsed_editorconfig) in
 999            self.local_editorconfig_settings(for_worktree)
1000        {
1001            if !for_path.starts_with(&directory_with_config) {
1002                properties.use_fallbacks();
1003                return Some(properties);
1004            }
1005            let parsed_editorconfig = parsed_editorconfig?;
1006            if parsed_editorconfig.is_root {
1007                properties = EditorconfigProperties::new();
1008            }
1009            for section in parsed_editorconfig.sections {
1010                section
1011                    .apply_to(&mut properties, for_path.as_std_path())
1012                    .log_err()?;
1013            }
1014        }
1015
1016        properties.use_fallbacks();
1017        Some(properties)
1018    }
1019}
1020
1021#[derive(Debug, Clone, PartialEq)]
1022pub enum InvalidSettingsError {
1023    LocalSettings { path: Arc<RelPath>, message: String },
1024    UserSettings { message: String },
1025    ServerSettings { message: String },
1026    DefaultSettings { message: String },
1027    Editorconfig { path: Arc<RelPath>, message: String },
1028    Tasks { path: PathBuf, message: String },
1029    Debug { path: PathBuf, message: String },
1030}
1031
1032impl std::fmt::Display for InvalidSettingsError {
1033    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1034        match self {
1035            InvalidSettingsError::LocalSettings { message, .. }
1036            | InvalidSettingsError::UserSettings { message }
1037            | InvalidSettingsError::ServerSettings { message }
1038            | InvalidSettingsError::DefaultSettings { message }
1039            | InvalidSettingsError::Tasks { message, .. }
1040            | InvalidSettingsError::Editorconfig { message, .. }
1041            | InvalidSettingsError::Debug { message, .. } => {
1042                write!(f, "{message}")
1043            }
1044        }
1045    }
1046}
1047impl std::error::Error for InvalidSettingsError {}
1048
1049impl Debug for SettingsStore {
1050    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1051        f.debug_struct("SettingsStore")
1052            .field(
1053                "types",
1054                &self
1055                    .setting_values
1056                    .values()
1057                    .map(|value| value.setting_type_name())
1058                    .collect::<Vec<_>>(),
1059            )
1060            .field("default_settings", &self.default_settings)
1061            .field("user_settings", &self.user_settings)
1062            .field("local_settings", &self.local_settings)
1063            .finish_non_exhaustive()
1064    }
1065}
1066
1067impl<T: Settings> AnySettingValue for SettingValue<T> {
1068    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any> {
1069        Box::new(T::from_settings(s)) as _
1070    }
1071
1072    fn setting_type_name(&self) -> &'static str {
1073        type_name::<T>()
1074    }
1075
1076    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)> {
1077        self.local_values
1078            .iter()
1079            .map(|(id, path, value)| (*id, path.clone(), value as _))
1080            .collect()
1081    }
1082
1083    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
1084        if let Some(SettingsLocation { worktree_id, path }) = path {
1085            for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
1086                if worktree_id == *settings_root_id && path.starts_with(settings_path) {
1087                    return value;
1088                }
1089            }
1090        }
1091
1092        self.global_value
1093            .as_ref()
1094            .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
1095    }
1096
1097    fn set_global_value(&mut self, value: Box<dyn Any>) {
1098        self.global_value = Some(*value.downcast().unwrap());
1099    }
1100
1101    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<RelPath>, value: Box<dyn Any>) {
1102        let value = *value.downcast().unwrap();
1103        match self
1104            .local_values
1105            .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1))
1106        {
1107            Ok(ix) => self.local_values[ix].2 = value,
1108            Err(ix) => self.local_values.insert(ix, (root_id, path, value)),
1109        }
1110    }
1111
1112    fn import_from_vscode(
1113        &self,
1114        vscode_settings: &VsCodeSettings,
1115        settings_content: &mut SettingsContent,
1116    ) {
1117        T::import_from_vscode(vscode_settings, settings_content);
1118    }
1119}
1120
1121#[cfg(test)]
1122mod tests {
1123    use std::num::NonZeroU32;
1124
1125    use crate::{
1126        ClosePosition, ItemSettingsContent, VsCodeSettingsSource, default_settings,
1127        settings_content::LanguageSettingsContent, test_settings,
1128    };
1129
1130    use super::*;
1131    use unindent::Unindent;
1132    use util::rel_path::rel_path;
1133
1134    #[derive(Debug, PartialEq)]
1135    struct AutoUpdateSetting {
1136        auto_update: bool,
1137    }
1138
1139    impl Settings for AutoUpdateSetting {
1140        fn from_settings(content: &SettingsContent) -> Self {
1141            AutoUpdateSetting {
1142                auto_update: content.auto_update.unwrap(),
1143            }
1144        }
1145    }
1146
1147    #[derive(Debug, PartialEq)]
1148    struct ItemSettings {
1149        close_position: ClosePosition,
1150        git_status: bool,
1151    }
1152
1153    impl Settings for ItemSettings {
1154        fn from_settings(content: &SettingsContent) -> Self {
1155            let content = content.tabs.clone().unwrap();
1156            ItemSettings {
1157                close_position: content.close_position.unwrap(),
1158                git_status: content.git_status.unwrap(),
1159            }
1160        }
1161
1162        fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
1163            let mut show = None;
1164
1165            vscode.bool_setting("workbench.editor.decorations.colors", &mut show);
1166            if let Some(show) = show {
1167                content
1168                    .tabs
1169                    .get_or_insert_default()
1170                    .git_status
1171                    .replace(show);
1172            }
1173        }
1174    }
1175
1176    #[derive(Debug, PartialEq)]
1177    struct DefaultLanguageSettings {
1178        tab_size: NonZeroU32,
1179        preferred_line_length: u32,
1180    }
1181
1182    impl Settings for DefaultLanguageSettings {
1183        fn from_settings(content: &SettingsContent) -> Self {
1184            let content = &content.project.all_languages.defaults;
1185            DefaultLanguageSettings {
1186                tab_size: content.tab_size.unwrap(),
1187                preferred_line_length: content.preferred_line_length.unwrap(),
1188            }
1189        }
1190
1191        fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
1192            let content = &mut content.project.all_languages.defaults;
1193
1194            if let Some(size) = vscode
1195                .read_value("editor.tabSize")
1196                .and_then(|v| v.as_u64())
1197                .and_then(|n| NonZeroU32::new(n as u32))
1198            {
1199                content.tab_size = Some(size);
1200            }
1201        }
1202    }
1203
1204    #[derive(Debug, PartialEq)]
1205    struct ThemeSettings {
1206        buffer_font_family: FontFamilyName,
1207        buffer_font_fallbacks: Vec<FontFamilyName>,
1208    }
1209
1210    impl Settings for ThemeSettings {
1211        fn from_settings(content: &SettingsContent) -> Self {
1212            let content = content.theme.clone();
1213            ThemeSettings {
1214                buffer_font_family: content.buffer_font_family.unwrap(),
1215                buffer_font_fallbacks: content.buffer_font_fallbacks.unwrap(),
1216            }
1217        }
1218
1219        fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
1220            let content = &mut content.theme;
1221
1222            vscode.font_family_setting(
1223                "editor.fontFamily",
1224                &mut content.buffer_font_family,
1225                &mut content.buffer_font_fallbacks,
1226            );
1227        }
1228    }
1229
1230    #[gpui::test]
1231    fn test_settings_store_basic(cx: &mut App) {
1232        let mut store = SettingsStore::new(cx, &default_settings());
1233        store.register_setting::<AutoUpdateSetting>();
1234        store.register_setting::<ItemSettings>();
1235        store.register_setting::<DefaultLanguageSettings>();
1236
1237        assert_eq!(
1238            store.get::<AutoUpdateSetting>(None),
1239            &AutoUpdateSetting { auto_update: true }
1240        );
1241        assert_eq!(
1242            store.get::<ItemSettings>(None).close_position,
1243            ClosePosition::Right
1244        );
1245
1246        store
1247            .set_user_settings(
1248                r#"{
1249                    "auto_update": false,
1250                    "tabs": {
1251                      "close_position": "left"
1252                    }
1253                }"#,
1254                cx,
1255            )
1256            .unwrap();
1257
1258        assert_eq!(
1259            store.get::<AutoUpdateSetting>(None),
1260            &AutoUpdateSetting { auto_update: false }
1261        );
1262        assert_eq!(
1263            store.get::<ItemSettings>(None).close_position,
1264            ClosePosition::Left
1265        );
1266
1267        store
1268            .set_local_settings(
1269                WorktreeId::from_usize(1),
1270                rel_path("root1").into(),
1271                LocalSettingsKind::Settings,
1272                Some(r#"{ "tab_size": 5 }"#),
1273                cx,
1274            )
1275            .unwrap();
1276        store
1277            .set_local_settings(
1278                WorktreeId::from_usize(1),
1279                rel_path("root1/subdir").into(),
1280                LocalSettingsKind::Settings,
1281                Some(r#"{ "preferred_line_length": 50 }"#),
1282                cx,
1283            )
1284            .unwrap();
1285
1286        store
1287            .set_local_settings(
1288                WorktreeId::from_usize(1),
1289                rel_path("root2").into(),
1290                LocalSettingsKind::Settings,
1291                Some(r#"{ "tab_size": 9, "auto_update": true}"#),
1292                cx,
1293            )
1294            .unwrap();
1295
1296        assert_eq!(
1297            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1298                worktree_id: WorktreeId::from_usize(1),
1299                path: rel_path("root1/something"),
1300            })),
1301            &DefaultLanguageSettings {
1302                preferred_line_length: 80,
1303                tab_size: 5.try_into().unwrap(),
1304            }
1305        );
1306        assert_eq!(
1307            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1308                worktree_id: WorktreeId::from_usize(1),
1309                path: rel_path("root1/subdir/something"),
1310            })),
1311            &DefaultLanguageSettings {
1312                preferred_line_length: 50,
1313                tab_size: 5.try_into().unwrap(),
1314            }
1315        );
1316        assert_eq!(
1317            store.get::<DefaultLanguageSettings>(Some(SettingsLocation {
1318                worktree_id: WorktreeId::from_usize(1),
1319                path: rel_path("root2/something"),
1320            })),
1321            &DefaultLanguageSettings {
1322                preferred_line_length: 80,
1323                tab_size: 9.try_into().unwrap(),
1324            }
1325        );
1326        assert_eq!(
1327            store.get::<AutoUpdateSetting>(Some(SettingsLocation {
1328                worktree_id: WorktreeId::from_usize(1),
1329                path: rel_path("root2/something")
1330            })),
1331            &AutoUpdateSetting { auto_update: false }
1332        );
1333    }
1334
1335    #[gpui::test]
1336    fn test_setting_store_assign_json_before_register(cx: &mut App) {
1337        let mut store = SettingsStore::new(cx, &test_settings());
1338        store
1339            .set_user_settings(r#"{ "auto_update": false }"#, cx)
1340            .unwrap();
1341        store.register_setting::<AutoUpdateSetting>();
1342
1343        assert_eq!(
1344            store.get::<AutoUpdateSetting>(None),
1345            &AutoUpdateSetting { auto_update: false }
1346        );
1347    }
1348
1349    #[track_caller]
1350    fn check_settings_update(
1351        store: &mut SettingsStore,
1352        old_json: String,
1353        update: fn(&mut SettingsContent),
1354        expected_new_json: String,
1355        cx: &mut App,
1356    ) {
1357        store.set_user_settings(&old_json, cx).ok();
1358        let edits = store.edits_for_update(&old_json, update);
1359        let mut new_json = old_json;
1360        for (range, replacement) in edits.into_iter() {
1361            new_json.replace_range(range, &replacement);
1362        }
1363        pretty_assertions::assert_eq!(new_json, expected_new_json);
1364    }
1365
1366    #[gpui::test]
1367    fn test_setting_store_update(cx: &mut App) {
1368        let mut store = SettingsStore::new(cx, &test_settings());
1369
1370        // entries added and updated
1371        check_settings_update(
1372            &mut store,
1373            r#"{
1374                "languages": {
1375                    "JSON": {
1376                        "auto_indent": true
1377                    }
1378                }
1379            }"#
1380            .unindent(),
1381            |settings| {
1382                settings
1383                    .languages_mut()
1384                    .get_mut("JSON")
1385                    .unwrap()
1386                    .auto_indent = Some(false);
1387
1388                settings.languages_mut().insert(
1389                    "Rust".into(),
1390                    LanguageSettingsContent {
1391                        auto_indent: Some(true),
1392                        ..Default::default()
1393                    },
1394                );
1395            },
1396            r#"{
1397                "languages": {
1398                    "Rust": {
1399                        "auto_indent": true
1400                    },
1401                    "JSON": {
1402                        "auto_indent": false
1403                    }
1404                }
1405            }"#
1406            .unindent(),
1407            cx,
1408        );
1409
1410        // entries removed
1411        check_settings_update(
1412            &mut store,
1413            r#"{
1414                "languages": {
1415                    "Rust": {
1416                        "language_setting_2": true
1417                    },
1418                    "JSON": {
1419                        "language_setting_1": false
1420                    }
1421                }
1422            }"#
1423            .unindent(),
1424            |settings| {
1425                settings.languages_mut().remove("JSON").unwrap();
1426            },
1427            r#"{
1428                "languages": {
1429                    "Rust": {
1430                        "language_setting_2": true
1431                    }
1432                }
1433            }"#
1434            .unindent(),
1435            cx,
1436        );
1437
1438        check_settings_update(
1439            &mut store,
1440            r#"{
1441                "languages": {
1442                    "Rust": {
1443                        "language_setting_2": true
1444                    },
1445                    "JSON": {
1446                        "language_setting_1": false
1447                    }
1448                }
1449            }"#
1450            .unindent(),
1451            |settings| {
1452                settings.languages_mut().remove("Rust").unwrap();
1453            },
1454            r#"{
1455                "languages": {
1456                    "JSON": {
1457                        "language_setting_1": false
1458                    }
1459                }
1460            }"#
1461            .unindent(),
1462            cx,
1463        );
1464
1465        // weird formatting
1466        check_settings_update(
1467            &mut store,
1468            r#"{
1469                "tabs":   { "close_position": "left", "name": "Max"  }
1470                }"#
1471            .unindent(),
1472            |settings| {
1473                settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left);
1474            },
1475            r#"{
1476                "tabs":   { "close_position": "left", "name": "Max"  }
1477                }"#
1478            .unindent(),
1479            cx,
1480        );
1481
1482        // single-line formatting, other keys
1483        check_settings_update(
1484            &mut store,
1485            r#"{ "one": 1, "two": 2 }"#.to_owned(),
1486            |settings| settings.auto_update = Some(true),
1487            r#"{ "auto_update": true, "one": 1, "two": 2 }"#.to_owned(),
1488            cx,
1489        );
1490
1491        // empty object
1492        check_settings_update(
1493            &mut store,
1494            r#"{
1495                "tabs": {}
1496            }"#
1497            .unindent(),
1498            |settings| settings.tabs.as_mut().unwrap().close_position = Some(ClosePosition::Left),
1499            r#"{
1500                "tabs": {
1501                    "close_position": "left"
1502                }
1503            }"#
1504            .unindent(),
1505            cx,
1506        );
1507
1508        // no content
1509        check_settings_update(
1510            &mut store,
1511            r#""#.unindent(),
1512            |settings| {
1513                settings.tabs = Some(ItemSettingsContent {
1514                    git_status: Some(true),
1515                    ..Default::default()
1516                })
1517            },
1518            r#"{
1519                "tabs": {
1520                    "git_status": true
1521                }
1522            }
1523            "#
1524            .unindent(),
1525            cx,
1526        );
1527
1528        check_settings_update(
1529            &mut store,
1530            r#"{
1531            }
1532            "#
1533            .unindent(),
1534            |settings| settings.title_bar.get_or_insert_default().show_branch_name = Some(true),
1535            r#"{
1536                "title_bar": {
1537                    "show_branch_name": true
1538                }
1539            }
1540            "#
1541            .unindent(),
1542            cx,
1543        );
1544    }
1545
1546    #[gpui::test]
1547    fn test_vscode_import(cx: &mut App) {
1548        let mut store = SettingsStore::new(cx, &test_settings());
1549        store.register_setting::<DefaultLanguageSettings>();
1550        store.register_setting::<ItemSettings>();
1551        store.register_setting::<AutoUpdateSetting>();
1552        store.register_setting::<ThemeSettings>();
1553
1554        // create settings that werent present
1555        check_vscode_import(
1556            &mut store,
1557            r#"{
1558            }
1559            "#
1560            .unindent(),
1561            r#" { "editor.tabSize": 37 } "#.to_owned(),
1562            r#"{
1563                "tab_size": 37
1564            }
1565            "#
1566            .unindent(),
1567            cx,
1568        );
1569
1570        // persist settings that were present
1571        check_vscode_import(
1572            &mut store,
1573            r#"{
1574                "preferred_line_length": 99,
1575            }
1576            "#
1577            .unindent(),
1578            r#"{ "editor.tabSize": 42 }"#.to_owned(),
1579            r#"{
1580                "tab_size": 42,
1581                "preferred_line_length": 99,
1582            }
1583            "#
1584            .unindent(),
1585            cx,
1586        );
1587
1588        // don't clobber settings that aren't present in vscode
1589        check_vscode_import(
1590            &mut store,
1591            r#"{
1592                "preferred_line_length": 99,
1593                "tab_size": 42
1594            }
1595            "#
1596            .unindent(),
1597            r#"{}"#.to_owned(),
1598            r#"{
1599                "preferred_line_length": 99,
1600                "tab_size": 42
1601            }
1602            "#
1603            .unindent(),
1604            cx,
1605        );
1606
1607        // custom enum
1608        check_vscode_import(
1609            &mut store,
1610            r#"{
1611            }
1612            "#
1613            .unindent(),
1614            r#"{ "workbench.editor.decorations.colors": true }"#.to_owned(),
1615            r#"{
1616                "tabs": {
1617                    "git_status": true
1618                }
1619            }
1620            "#
1621            .unindent(),
1622            cx,
1623        );
1624
1625        // font-family
1626        check_vscode_import(
1627            &mut store,
1628            r#"{
1629            }
1630            "#
1631            .unindent(),
1632            r#"{ "editor.fontFamily": "Cascadia Code, 'Consolas', Courier New" }"#.to_owned(),
1633            r#"{
1634                "buffer_font_fallbacks": [
1635                    "Consolas",
1636                    "Courier New"
1637                ],
1638                "buffer_font_family": "Cascadia Code"
1639            }
1640            "#
1641            .unindent(),
1642            cx,
1643        );
1644    }
1645
1646    #[track_caller]
1647    fn check_vscode_import(
1648        store: &mut SettingsStore,
1649        old: String,
1650        vscode: String,
1651        expected: String,
1652        cx: &mut App,
1653    ) {
1654        store.set_user_settings(&old, cx).ok();
1655        let new = store.get_vscode_edits(
1656            old,
1657            &VsCodeSettings::from_str(&vscode, VsCodeSettingsSource::VsCode).unwrap(),
1658        );
1659        pretty_assertions::assert_eq!(new, expected);
1660    }
1661
1662    #[gpui::test]
1663    fn test_update_git_settings(cx: &mut App) {
1664        let store = SettingsStore::new(cx, &test_settings());
1665
1666        let actual = store.new_text_for_update("{}".to_string(), |current| {
1667            current
1668                .git
1669                .get_or_insert_default()
1670                .inline_blame
1671                .get_or_insert_default()
1672                .enabled = Some(true);
1673        });
1674        assert_eq!(
1675            actual,
1676            r#"{
1677            "git": {
1678                "inline_blame": {
1679                    "enabled": true
1680                }
1681            }
1682        }
1683        "#
1684            .unindent()
1685        );
1686    }
1687
1688    #[gpui::test]
1689    fn test_global_settings(cx: &mut App) {
1690        let mut store = SettingsStore::new(cx, &test_settings());
1691        store.register_setting::<ItemSettings>();
1692
1693        // Set global settings - these should override defaults but not user settings
1694        store
1695            .set_global_settings(
1696                r#"{
1697                    "tabs": {
1698                        "close_position": "right",
1699                        "git_status": true,
1700                    }
1701                }"#,
1702                cx,
1703            )
1704            .unwrap();
1705
1706        // Before user settings, global settings should apply
1707        assert_eq!(
1708            store.get::<ItemSettings>(None),
1709            &ItemSettings {
1710                close_position: ClosePosition::Right,
1711                git_status: true,
1712            }
1713        );
1714
1715        // Set user settings - these should override both defaults and global
1716        store
1717            .set_user_settings(
1718                r#"{
1719                    "tabs": {
1720                        "close_position": "left"
1721                    }
1722                }"#,
1723                cx,
1724            )
1725            .unwrap();
1726
1727        // User settings should override global settings
1728        assert_eq!(
1729            store.get::<ItemSettings>(None),
1730            &ItemSettings {
1731                close_position: ClosePosition::Left,
1732                git_status: true, // Staff from global settings
1733            }
1734        );
1735    }
1736
1737    #[gpui::test]
1738    fn test_get_value_for_field_basic(cx: &mut App) {
1739        let mut store = SettingsStore::new(cx, &test_settings());
1740        store.register_setting::<DefaultLanguageSettings>();
1741
1742        store
1743            .set_user_settings(r#"{"preferred_line_length": 0}"#, cx)
1744            .unwrap();
1745        let local = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
1746        store
1747            .set_local_settings(
1748                local.0,
1749                local.1.clone(),
1750                LocalSettingsKind::Settings,
1751                Some(r#"{}"#),
1752                cx,
1753            )
1754            .unwrap();
1755
1756        fn get(content: &SettingsContent) -> &Option<u32> {
1757            &content.project.all_languages.defaults.preferred_line_length
1758        }
1759
1760        let default_value = get(&store.default_settings).unwrap();
1761
1762        assert_eq!(
1763            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1764            (SettingsFile::User, Some(&0))
1765        );
1766        assert_eq!(
1767            store.get_value_from_file(SettingsFile::User, get),
1768            (SettingsFile::User, Some(&0))
1769        );
1770        store.set_user_settings(r#"{}"#, cx).unwrap();
1771        assert_eq!(
1772            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1773            (SettingsFile::Default, Some(&default_value))
1774        );
1775        store
1776            .set_local_settings(
1777                local.0,
1778                local.1.clone(),
1779                LocalSettingsKind::Settings,
1780                Some(r#"{"preferred_line_length": 80}"#),
1781                cx,
1782            )
1783            .unwrap();
1784        assert_eq!(
1785            store.get_value_from_file(SettingsFile::Project(local.clone()), get),
1786            (SettingsFile::Project(local), Some(&80))
1787        );
1788        assert_eq!(
1789            store.get_value_from_file(SettingsFile::User, get),
1790            (SettingsFile::Default, Some(&default_value))
1791        );
1792    }
1793
1794    #[gpui::test]
1795    fn test_get_value_for_field_local_worktrees_dont_interfere(cx: &mut App) {
1796        let mut store = SettingsStore::new(cx, &test_settings());
1797        store.register_setting::<DefaultLanguageSettings>();
1798        store.register_setting::<AutoUpdateSetting>();
1799
1800        let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
1801
1802        let local_1_child = (
1803            WorktreeId::from_usize(0),
1804            RelPath::new(
1805                std::path::Path::new("child1"),
1806                util::paths::PathStyle::Posix,
1807            )
1808            .unwrap()
1809            .into_arc(),
1810        );
1811
1812        let local_2 = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
1813        let local_2_child = (
1814            WorktreeId::from_usize(1),
1815            RelPath::new(
1816                std::path::Path::new("child2"),
1817                util::paths::PathStyle::Posix,
1818            )
1819            .unwrap()
1820            .into_arc(),
1821        );
1822
1823        fn get(content: &SettingsContent) -> &Option<u32> {
1824            &content.project.all_languages.defaults.preferred_line_length
1825        }
1826
1827        store
1828            .set_local_settings(
1829                local_1.0,
1830                local_1.1.clone(),
1831                LocalSettingsKind::Settings,
1832                Some(r#"{"preferred_line_length": 1}"#),
1833                cx,
1834            )
1835            .unwrap();
1836        store
1837            .set_local_settings(
1838                local_1_child.0,
1839                local_1_child.1.clone(),
1840                LocalSettingsKind::Settings,
1841                Some(r#"{}"#),
1842                cx,
1843            )
1844            .unwrap();
1845        store
1846            .set_local_settings(
1847                local_2.0,
1848                local_2.1.clone(),
1849                LocalSettingsKind::Settings,
1850                Some(r#"{"preferred_line_length": 2}"#),
1851                cx,
1852            )
1853            .unwrap();
1854        store
1855            .set_local_settings(
1856                local_2_child.0,
1857                local_2_child.1.clone(),
1858                LocalSettingsKind::Settings,
1859                Some(r#"{}"#),
1860                cx,
1861            )
1862            .unwrap();
1863
1864        // each local child should only inherit from it's parent
1865        assert_eq!(
1866            store.get_value_from_file(SettingsFile::Project(local_2_child), get),
1867            (SettingsFile::Project(local_2), Some(&2))
1868        );
1869        assert_eq!(
1870            store.get_value_from_file(SettingsFile::Project(local_1_child.clone()), get),
1871            (SettingsFile::Project(local_1.clone()), Some(&1))
1872        );
1873
1874        // adjacent children should be treated as siblings not inherit from each other
1875        let local_1_adjacent_child = (local_1.0, rel_path("adjacent_child").into_arc());
1876        store
1877            .set_local_settings(
1878                local_1_adjacent_child.0,
1879                local_1_adjacent_child.1.clone(),
1880                LocalSettingsKind::Settings,
1881                Some(r#"{}"#),
1882                cx,
1883            )
1884            .unwrap();
1885        store
1886            .set_local_settings(
1887                local_1_child.0,
1888                local_1_child.1.clone(),
1889                LocalSettingsKind::Settings,
1890                Some(r#"{"preferred_line_length": 3}"#),
1891                cx,
1892            )
1893            .unwrap();
1894
1895        assert_eq!(
1896            store.get_value_from_file(SettingsFile::Project(local_1_adjacent_child.clone()), get),
1897            (SettingsFile::Project(local_1.clone()), Some(&1))
1898        );
1899        store
1900            .set_local_settings(
1901                local_1_adjacent_child.0,
1902                local_1_adjacent_child.1,
1903                LocalSettingsKind::Settings,
1904                Some(r#"{"preferred_line_length": 3}"#),
1905                cx,
1906            )
1907            .unwrap();
1908        store
1909            .set_local_settings(
1910                local_1_child.0,
1911                local_1_child.1.clone(),
1912                LocalSettingsKind::Settings,
1913                Some(r#"{}"#),
1914                cx,
1915            )
1916            .unwrap();
1917        assert_eq!(
1918            store.get_value_from_file(SettingsFile::Project(local_1_child), get),
1919            (SettingsFile::Project(local_1), Some(&1))
1920        );
1921    }
1922
1923    #[gpui::test]
1924    fn test_get_overrides_for_field(cx: &mut App) {
1925        let mut store = SettingsStore::new(cx, &test_settings());
1926        store.register_setting::<DefaultLanguageSettings>();
1927
1928        let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
1929        let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc());
1930        let wt0_child2 = (WorktreeId::from_usize(0), rel_path("child2").into_arc());
1931
1932        let wt1_root = (WorktreeId::from_usize(1), RelPath::empty().into_arc());
1933        let wt1_subdir = (WorktreeId::from_usize(1), rel_path("subdir").into_arc());
1934
1935        fn get(content: &SettingsContent) -> &Option<u32> {
1936            &content.project.all_languages.defaults.preferred_line_length
1937        }
1938
1939        store
1940            .set_user_settings(r#"{"preferred_line_length": 100}"#, cx)
1941            .unwrap();
1942
1943        store
1944            .set_local_settings(
1945                wt0_root.0,
1946                wt0_root.1.clone(),
1947                LocalSettingsKind::Settings,
1948                Some(r#"{"preferred_line_length": 80}"#),
1949                cx,
1950            )
1951            .unwrap();
1952        store
1953            .set_local_settings(
1954                wt0_child1.0,
1955                wt0_child1.1.clone(),
1956                LocalSettingsKind::Settings,
1957                Some(r#"{"preferred_line_length": 120}"#),
1958                cx,
1959            )
1960            .unwrap();
1961        store
1962            .set_local_settings(
1963                wt0_child2.0,
1964                wt0_child2.1.clone(),
1965                LocalSettingsKind::Settings,
1966                Some(r#"{}"#),
1967                cx,
1968            )
1969            .unwrap();
1970
1971        store
1972            .set_local_settings(
1973                wt1_root.0,
1974                wt1_root.1.clone(),
1975                LocalSettingsKind::Settings,
1976                Some(r#"{"preferred_line_length": 90}"#),
1977                cx,
1978            )
1979            .unwrap();
1980        store
1981            .set_local_settings(
1982                wt1_subdir.0,
1983                wt1_subdir.1.clone(),
1984                LocalSettingsKind::Settings,
1985                Some(r#"{}"#),
1986                cx,
1987            )
1988            .unwrap();
1989
1990        let overrides = store.get_overrides_for_field(SettingsFile::Default, get);
1991        assert_eq!(
1992            overrides,
1993            vec![
1994                SettingsFile::User,
1995                SettingsFile::Project(wt0_root.clone()),
1996                SettingsFile::Project(wt0_child1.clone()),
1997                SettingsFile::Project(wt1_root.clone()),
1998            ]
1999        );
2000
2001        let overrides = store.get_overrides_for_field(SettingsFile::User, get);
2002        assert_eq!(
2003            overrides,
2004            vec![
2005                SettingsFile::Project(wt0_root.clone()),
2006                SettingsFile::Project(wt0_child1.clone()),
2007                SettingsFile::Project(wt1_root.clone()),
2008            ]
2009        );
2010
2011        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_root), get);
2012        assert_eq!(overrides, vec![]);
2013
2014        let overrides =
2015            store.get_overrides_for_field(SettingsFile::Project(wt0_child1.clone()), get);
2016        assert_eq!(overrides, vec![]);
2017
2018        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child2), get);
2019        assert_eq!(overrides, vec![]);
2020
2021        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_root), get);
2022        assert_eq!(overrides, vec![]);
2023
2024        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt1_subdir), get);
2025        assert_eq!(overrides, vec![]);
2026
2027        let wt0_deep_child = (
2028            WorktreeId::from_usize(0),
2029            rel_path("child1/subdir").into_arc(),
2030        );
2031        store
2032            .set_local_settings(
2033                wt0_deep_child.0,
2034                wt0_deep_child.1.clone(),
2035                LocalSettingsKind::Settings,
2036                Some(r#"{"preferred_line_length": 140}"#),
2037                cx,
2038            )
2039            .unwrap();
2040
2041        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_deep_child), get);
2042        assert_eq!(overrides, vec![]);
2043
2044        let overrides = store.get_overrides_for_field(SettingsFile::Project(wt0_child1), get);
2045        assert_eq!(overrides, vec![]);
2046    }
2047}