settings_store.rs

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