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