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