use anyhow::{Context as _, Result};use collections::{BTreeMap, HashMap, btree_map, hash_map};use ec4rs::{ConfigParser, PropertiesSource, Section};use fs::Fs;use futures::{FutureExt, StreamExt, channel::mpsc, future::LocalBoxFuture};use gpui::{App, AsyncApp, BorrowAppContext, Global, Task, UpdateGlobal}; use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema};use serde::{Deserialize, Serialize, de::DeserializeOwned};use serde_json::Value;use smallvec::SmallVec;use std::{ any::{Any, TypeId, type_name}, fmt::Debug, ops::Range, path::{Path, PathBuf}, str::{self, FromStr}, sync::{Arc, LazyLock},};use streaming_iterator::StreamingIterator;use tree_sitter::Query;use util::RangeExt; use util::{ResultExt as _, merge_non_null_json_value_into}; pub type EditorconfigProperties = ec4rs::Properties; use crate::{SettingsJsonSchemaParams, VsCodeSettings, WorktreeId}; /// A value that can be defined as a user setting.////// Settings can be loaded from a combination of multiple JSON files.pub trait Settings: 'static + Send + Sync { /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. const KEY: Option<&'static str>; const FALLBACK_KEY: Option<&'static str> = None; /// The name of the keys in the [`FileContent`](Self::FileContent) that should /// always be written to a settings file, even if their value matches the default /// value. /// /// This is useful for tagged [`FileContent`](Self::FileContent)s where the tag /// is a "version" field that should always be persisted, even if the current /// user settings match the current version of the settings. const PRESERVED_KEYS: Option<&'static [&'static str]> = None; /// The type that is stored in an individual JSON file. type FileContent: Clone + Default + Serialize + DeserializeOwned + JsonSchema; /// The logic for combining together values from one or more JSON files into the /// final value for this setting. fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self> where Self: Sized; fn json_schema( generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams, _: &App, ) -> RootSchema { generator.root_schema_for::<Self::FileContent>() } fn missing_default() -> anyhow::Error { anyhow::anyhow!("missing default") } /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known /// equivalent settings from a vscode config to our config fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent); #[track_caller] fn register(cx: &mut App) where Self: Sized, { SettingsStore::update_global(cx, |store, cx| { store.register_setting::<Self>(cx); }); } #[track_caller] fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self where Self: Sized, { cx.global::<SettingsStore>().get(path) } #[track_caller] fn get_global(cx: &App) -> &Self where Self: Sized, { cx.global::<SettingsStore>().get(None) } #[track_caller] fn try_read_global<R>(cx: &AsyncApp, f: impl FnOnce(&Self) -> R) -> Option<R> where Self: Sized, { cx.try_read_global(|s: &SettingsStore, _| f(s.get(None))) } #[track_caller] fn override_global(settings: Self, cx: &mut App) where Self: Sized, { cx.global_mut::<SettingsStore>().override_global(settings) }} #[derive(Clone, Copy, Debug)]pub struct SettingsSources<'a, T> { /// The default Zed settings. pub default: &'a T, /// Global settings (loaded before user settings). pub global: Option<&'a T>, /// Settings provided by extensions. pub extensions: Option<&'a T>, /// The user settings. pub user: Option<&'a T>, /// The user settings for the current release channel. pub release_channel: Option<&'a T>, /// The server's settings. pub server: Option<&'a T>, /// The project settings, ordered from least specific to most specific. pub project: &'a [&'a T],} impl<'a, T: Serialize> SettingsSources<'a, T> { /// Returns an iterator over the default settings as well as all settings customizations. pub fn defaults_and_customizations(&self) -> impl Iterator<Item = &T> { [self.default].into_iter().chain(self.customizations()) } /// Returns an iterator over all of the settings customizations. pub fn customizations(&self) -> impl Iterator<Item = &T> { self.global .into_iter() .chain(self.extensions) .chain(self.user) .chain(self.release_channel) .chain(self.server) .chain(self.project.iter().copied()) } /// Returns the settings after performing a JSON merge of the provided customizations. /// /// Customizations later in the iterator win out over the earlier ones. pub fn json_merge_with<O: DeserializeOwned>( customizations: impl Iterator<Item = &'a T>, ) -> Result<O> { let mut merged = Value::Null; for value in customizations { merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); } Ok(serde_json::from_value(merged)?) } /// Returns the settings after performing a JSON merge of the customizations into the /// default settings. /// /// More-specific customizations win out over the less-specific ones. pub fn json_merge<O: DeserializeOwned>(&'a self) -> Result<O> { Self::json_merge_with(self.defaults_and_customizations()) }} #[derive(Clone, Copy, Debug)]pub struct SettingsLocation<'a> { pub worktree_id: WorktreeId, pub path: &'a Path,} /// A set of strongly-typed setting values defined via multiple config files.pub struct SettingsStore { setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>, raw_default_settings: Value, raw_global_settings: Option<Value>, raw_user_settings: Value, raw_server_settings: Option<Value>, raw_extension_settings: Value, raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), Value>, raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>, tab_size_callback: Option<( TypeId, Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>, )>, _setting_file_updates: Task<()>, setting_file_updates_tx: mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,} #[derive(Clone)]pub struct Editorconfig { pub is_root: bool, pub sections: SmallVec<[Section; 5]>,} impl FromStr for Editorconfig { type Err = anyhow::Error; fn from_str(contents: &str) -> Result<Self, Self::Err> { let parser = ConfigParser::new_buffered(contents.as_bytes()) .context("creating editorconfig parser")?; let is_root = parser.is_root; let sections = parser .collect::<Result<SmallVec<_>, _>>() .context("parsing editorconfig sections")?; Ok(Self { is_root, sections }) }} #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]pub enum LocalSettingsKind { Settings, Tasks, Editorconfig, Debug,} impl Global for SettingsStore {} #[derive(Debug)]struct SettingValue<T> { global_value: Option<T>, local_values: Vec<(WorktreeId, Arc<Path>, T)>,} trait AnySettingValue: 'static + Send + Sync { fn key(&self) -> Option<&'static str>; fn setting_type_name(&self) -> &'static str; fn deserialize_setting(&self, json: &Value) -> Result<DeserializedSetting> { self.deserialize_setting_with_key(json).1 } fn deserialize_setting_with_key( &self, json: &Value, ) -> (Option<&'static str>, Result<DeserializedSetting>); fn load_setting( &self, sources: SettingsSources<DeserializedSetting>, cx: &mut App, ) -> Result<Box<dyn Any>>; fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any; fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>; fn set_global_value(&mut self, value: Box<dyn Any>); fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>); fn json_schema( &self, generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams, cx: &App, ) -> RootSchema; fn edits_for_update( &self, raw_settings: &serde_json::Value, tab_size: usize, vscode_settings: &VsCodeSettings, text: &mut String, edits: &mut Vec<(Range<usize>, String)>, );} struct DeserializedSetting(Box<dyn Any>); impl SettingsStore { pub fn new(cx: &App) -> Self { let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded(); Self { setting_values: Default::default(), raw_default_settings: serde_json::json!({}), raw_global_settings: None, raw_user_settings: serde_json::json!({}), raw_server_settings: None, raw_extension_settings: serde_json::json!({}), raw_local_settings: Default::default(), raw_editorconfig_settings: BTreeMap::default(), tab_size_callback: Default::default(), setting_file_updates_tx, _setting_file_updates: cx.spawn(async move |cx| { while let Some(setting_file_update) = setting_file_updates_rx.next().await { (setting_file_update)(cx.clone()).await.log_err(); } }), } } pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R where C: BorrowAppContext, { cx.update_global(f) } /// Add a new type of setting to the store. pub fn register_setting<T: Settings>(&mut self, cx: &mut App) { let setting_type_id = TypeId::of::<T>(); let entry = self.setting_values.entry(setting_type_id); if matches!(entry, hash_map::Entry::Occupied(_)) { return; } let setting_value = entry.or_insert(Box::new(SettingValue::<T> { global_value: None, local_values: Vec::new(), })); if let Some(default_settings) = setting_value .deserialize_setting(&self.raw_default_settings) .log_err() { let user_value = setting_value .deserialize_setting(&self.raw_user_settings) .log_err(); let mut release_channel_value = None; if let Some(release_settings) = &self .raw_user_settings .get(release_channel::RELEASE_CHANNEL.dev_name()) { release_channel_value = setting_value .deserialize_setting(release_settings) .log_err(); } let server_value = self .raw_server_settings .as_ref() .and_then(|server_setting| { setting_value.deserialize_setting(server_setting).log_err() }); let extension_value = setting_value .deserialize_setting(&self.raw_extension_settings) .log_err(); if let Some(setting) = setting_value .load_setting( SettingsSources { default: &default_settings, global: None, extensions: extension_value.as_ref(), user: user_value.as_ref(), release_channel: release_channel_value.as_ref(), server: server_value.as_ref(), project: &[], }, cx, ) .context("A default setting must be added to the `default.json` file") .log_err() { setting_value.set_global_value(setting); } } } /// Get the value of a setting. /// /// Panics if the given setting type has not been registered, or if there is no /// value for this setting. pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T { self.setting_values .get(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .value_for_path(path) .downcast_ref::<T>() .expect("no default value for setting type") } /// Get all values from project specific settings pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<Path>, &T)> { self.setting_values .get(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .all_local_values() .into_iter() .map(|(id, path, any)| { ( id, path, any.downcast_ref::<T>() .expect("wrong value type for setting"), ) }) .collect() } /// Override the global value for a setting. /// /// The given value will be overwritten if the user settings file changes. pub fn override_global<T: Settings>(&mut self, value: T) { self.setting_values .get_mut(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .set_global_value(Box::new(value)) } /// Get the user's settings as a raw JSON value. /// /// For user-facing functionality use the typed setting interface. /// (e.g. ProjectSettings::get_global(cx)) pub fn raw_user_settings(&self) -> &Value { &self.raw_user_settings } /// Access the raw JSON value of the global settings. pub fn raw_global_settings(&self) -> Option<&Value> { self.raw_global_settings.as_ref() } #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut App) -> Self { let mut this = Self::new(cx); this.set_default_settings(&crate::test_settings(), cx) .unwrap(); this.set_user_settings("{}", cx).unwrap(); this } /// Updates the value of a setting in the user's global configuration. /// /// This is only for tests. Normally, settings are only loaded from /// JSON files. #[cfg(any(test, feature = "test-support"))] pub fn update_user_settings<T: Settings>( &mut self, cx: &mut App, update: impl FnOnce(&mut T::FileContent), ) { let old_text = serde_json::to_string(&self.raw_user_settings).unwrap(); let new_text = self.new_text_for_update::<T>(old_text, update); self.set_user_settings(&new_text, cx).unwrap(); } pub async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> { match fs.load(paths::settings_file()).await { result @ Ok(_) => result, Err(err) => { if let Some(e) = err.downcast_ref::<std::io::Error>() { if e.kind() == std::io::ErrorKind::NotFound { return Ok(crate::initial_user_settings_content().to_string()); } } Err(err) } } } pub async fn load_global_settings(fs: &Arc<dyn Fs>) -> Result<String> { match fs.load(paths::global_settings_file()).await { result @ Ok(_) => result, Err(err) => { if let Some(e) = err.downcast_ref::<std::io::Error>() { if e.kind() == std::io::ErrorKind::NotFound { return Ok("{}".to_string()); } } Err(err) } } } pub fn update_settings_file<T: Settings>( &self, fs: Arc<dyn Fs>, update: impl 'static + Send + FnOnce(&mut T::FileContent, &App), ) { self.setting_file_updates_tx .unbounded_send(Box::new(move |cx: AsyncApp| { async move { let old_text = Self::load_settings(&fs).await?; let new_text = cx.read_global(|store: &SettingsStore, cx| { store.new_text_for_update::<T>(old_text, |content| update(content, cx)) })?; let settings_path = paths::settings_file().as_path(); if fs.is_file(settings_path).await { let resolved_path = fs.canonicalize(settings_path).await.with_context(|| { format!("Failed to canonicalize settings path {:?}", settings_path) })?; fs.atomic_write(resolved_path.clone(), new_text) .await .with_context(|| { format!("Failed to write settings to file {:?}", resolved_path) })?; } else { fs.atomic_write(settings_path.to_path_buf(), new_text) .await .with_context(|| {Lines 1–500 of 2267. Load more
use anyhow::{Context as _, Result}; use collections::{BTreeMap, HashMap, btree_map, hash_map}; use ec4rs::{ConfigParser, PropertiesSource, Section}; use fs::Fs; use futures::{FutureExt, StreamExt, channel::mpsc, future::LocalBoxFuture}; use gpui::{App, AsyncApp, BorrowAppContext, Global, Task, UpdateGlobal}; use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name}; use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::Value; use smallvec::SmallVec; use std::{ any::{Any, TypeId, type_name}, fmt::Debug, ops::Range, path::{Path, PathBuf}, str::{self, FromStr}, sync::{Arc, LazyLock}, }; use streaming_iterator::StreamingIterator; use tree_sitter::Query; use util::RangeExt; use util::{ResultExt as _, merge_non_null_json_value_into}; pub type EditorconfigProperties = ec4rs::Properties; use crate::{SettingsJsonSchemaParams, VsCodeSettings, WorktreeId}; /// A value that can be defined as a user setting. /// /// Settings can be loaded from a combination of multiple JSON files. pub trait Settings: 'static + Send + Sync { /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. const KEY: Option<&'static str>; const FALLBACK_KEY: Option<&'static str> = None; /// The name of the keys in the [`FileContent`](Self::FileContent) that should /// always be written to a settings file, even if their value matches the default /// value. /// /// This is useful for tagged [`FileContent`](Self::FileContent)s where the tag /// is a "version" field that should always be persisted, even if the current /// user settings match the current version of the settings. const PRESERVED_KEYS: Option<&'static [&'static str]> = None; /// The type that is stored in an individual JSON file. type FileContent: Clone + Default + Serialize + DeserializeOwned + JsonSchema; /// The logic for combining together values from one or more JSON files into the /// final value for this setting. fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self> where Self: Sized; fn json_schema( generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams, _: &App, ) -> RootSchema { generator.root_schema_for::<Self::FileContent>() } fn missing_default() -> anyhow::Error { anyhow::anyhow!("missing default") } /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known /// equivalent settings from a vscode config to our config fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent); #[track_caller] fn register(cx: &mut App) where Self: Sized, { SettingsStore::update_global(cx, |store, cx| { store.register_setting::<Self>(cx); }); } #[track_caller] fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self where Self: Sized, { cx.global::<SettingsStore>().get(path) } #[track_caller] fn get_global(cx: &App) -> &Self where Self: Sized, { cx.global::<SettingsStore>().get(None) } #[track_caller] fn try_read_global<R>(cx: &AsyncApp, f: impl FnOnce(&Self) -> R) -> Option<R> where Self: Sized, { cx.try_read_global(|s: &SettingsStore, _| f(s.get(None))) } #[track_caller] fn override_global(settings: Self, cx: &mut App) where Self: Sized, { cx.global_mut::<SettingsStore>().override_global(settings) } } #[derive(Clone, Copy, Debug)] pub struct SettingsSources<'a, T> { /// The default Zed settings. pub default: &'a T, /// Global settings (loaded before user settings). pub global: Option<&'a T>, /// Settings provided by extensions. pub extensions: Option<&'a T>, /// The user settings. pub user: Option<&'a T>, /// The user settings for the current release channel. pub release_channel: Option<&'a T>, /// The server's settings. pub server: Option<&'a T>, /// The project settings, ordered from least specific to most specific. pub project: &'a [&'a T], } impl<'a, T: Serialize> SettingsSources<'a, T> { /// Returns an iterator over the default settings as well as all settings customizations. pub fn defaults_and_customizations(&self) -> impl Iterator<Item = &T> { [self.default].into_iter().chain(self.customizations()) } /// Returns an iterator over all of the settings customizations. pub fn customizations(&self) -> impl Iterator<Item = &T> { self.global .into_iter() .chain(self.extensions) .chain(self.user) .chain(self.release_channel) .chain(self.server) .chain(self.project.iter().copied()) } /// Returns the settings after performing a JSON merge of the provided customizations. /// /// Customizations later in the iterator win out over the earlier ones. pub fn json_merge_with<O: DeserializeOwned>( customizations: impl Iterator<Item = &'a T>, ) -> Result<O> { let mut merged = Value::Null; for value in customizations { merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); } Ok(serde_json::from_value(merged)?) } /// Returns the settings after performing a JSON merge of the customizations into the /// default settings. /// /// More-specific customizations win out over the less-specific ones. pub fn json_merge<O: DeserializeOwned>(&'a self) -> Result<O> { Self::json_merge_with(self.defaults_and_customizations()) } } #[derive(Clone, Copy, Debug)] pub struct SettingsLocation<'a> { pub worktree_id: WorktreeId, pub path: &'a Path, } /// A set of strongly-typed setting values defined via multiple config files. pub struct SettingsStore { setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>, raw_default_settings: Value, raw_global_settings: Option<Value>, raw_user_settings: Value, raw_server_settings: Option<Value>, raw_extension_settings: Value, raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), Value>, raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>, tab_size_callback: Option<( TypeId, Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>, )>, _setting_file_updates: Task<()>, setting_file_updates_tx: mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>, } #[derive(Clone)] pub struct Editorconfig { pub is_root: bool, pub sections: SmallVec<[Section; 5]>, } impl FromStr for Editorconfig { type Err = anyhow::Error; fn from_str(contents: &str) -> Result<Self, Self::Err> { let parser = ConfigParser::new_buffered(contents.as_bytes()) .context("creating editorconfig parser")?; let is_root = parser.is_root; let sections = parser .collect::<Result<SmallVec<_>, _>>() .context("parsing editorconfig sections")?; Ok(Self { is_root, sections }) } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum LocalSettingsKind { Settings, Tasks, Editorconfig, Debug, } impl Global for SettingsStore {} #[derive(Debug)] struct SettingValue<T> { global_value: Option<T>, local_values: Vec<(WorktreeId, Arc<Path>, T)>, } trait AnySettingValue: 'static + Send + Sync { fn key(&self) -> Option<&'static str>; fn setting_type_name(&self) -> &'static str; fn deserialize_setting(&self, json: &Value) -> Result<DeserializedSetting> { self.deserialize_setting_with_key(json).1 } fn deserialize_setting_with_key( &self, json: &Value, ) -> (Option<&'static str>, Result<DeserializedSetting>); fn load_setting( &self, sources: SettingsSources<DeserializedSetting>, cx: &mut App, ) -> Result<Box<dyn Any>>; fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any; fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>; fn set_global_value(&mut self, value: Box<dyn Any>); fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>); fn json_schema( &self, generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams, cx: &App, ) -> RootSchema; fn edits_for_update( &self, raw_settings: &serde_json::Value, tab_size: usize, vscode_settings: &VsCodeSettings, text: &mut String, edits: &mut Vec<(Range<usize>, String)>, ); } struct DeserializedSetting(Box<dyn Any>); impl SettingsStore { pub fn new(cx: &App) -> Self { let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded(); Self { setting_values: Default::default(), raw_default_settings: serde_json::json!({}), raw_global_settings: None, raw_user_settings: serde_json::json!({}), raw_server_settings: None, raw_extension_settings: serde_json::json!({}), raw_local_settings: Default::default(), raw_editorconfig_settings: BTreeMap::default(), tab_size_callback: Default::default(), setting_file_updates_tx, _setting_file_updates: cx.spawn(async move |cx| { while let Some(setting_file_update) = setting_file_updates_rx.next().await { (setting_file_update)(cx.clone()).await.log_err(); } }), } } pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R where C: BorrowAppContext, { cx.update_global(f) } /// Add a new type of setting to the store. pub fn register_setting<T: Settings>(&mut self, cx: &mut App) { let setting_type_id = TypeId::of::<T>(); let entry = self.setting_values.entry(setting_type_id); if matches!(entry, hash_map::Entry::Occupied(_)) { return; } let setting_value = entry.or_insert(Box::new(SettingValue::<T> { global_value: None, local_values: Vec::new(), })); if let Some(default_settings) = setting_value .deserialize_setting(&self.raw_default_settings) .log_err() { let user_value = setting_value .deserialize_setting(&self.raw_user_settings) .log_err(); let mut release_channel_value = None; if let Some(release_settings) = &self .raw_user_settings .get(release_channel::RELEASE_CHANNEL.dev_name()) { release_channel_value = setting_value .deserialize_setting(release_settings) .log_err(); } let server_value = self .raw_server_settings .as_ref() .and_then(|server_setting| { setting_value.deserialize_setting(server_setting).log_err() }); let extension_value = setting_value .deserialize_setting(&self.raw_extension_settings) .log_err(); if let Some(setting) = setting_value .load_setting( SettingsSources { default: &default_settings, global: None, extensions: extension_value.as_ref(), user: user_value.as_ref(), release_channel: release_channel_value.as_ref(), server: server_value.as_ref(), project: &[], }, cx, ) .context("A default setting must be added to the `default.json` file") .log_err() { setting_value.set_global_value(setting); } } } /// Get the value of a setting. /// /// Panics if the given setting type has not been registered, or if there is no /// value for this setting. pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T { self.setting_values .get(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .value_for_path(path) .downcast_ref::<T>() .expect("no default value for setting type") } /// Get all values from project specific settings pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<Path>, &T)> { self.setting_values .get(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .all_local_values() .into_iter() .map(|(id, path, any)| { ( id, path, any.downcast_ref::<T>() .expect("wrong value type for setting"), ) }) .collect() } /// Override the global value for a setting. /// /// The given value will be overwritten if the user settings file changes. pub fn override_global<T: Settings>(&mut self, value: T) { self.setting_values .get_mut(&TypeId::of::<T>()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .set_global_value(Box::new(value)) } /// Get the user's settings as a raw JSON value. /// /// For user-facing functionality use the typed setting interface. /// (e.g. ProjectSettings::get_global(cx)) pub fn raw_user_settings(&self) -> &Value { &self.raw_user_settings } /// Access the raw JSON value of the global settings. pub fn raw_global_settings(&self) -> Option<&Value> { self.raw_global_settings.as_ref() } #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut App) -> Self { let mut this = Self::new(cx); this.set_default_settings(&crate::test_settings(), cx) .unwrap(); this.set_user_settings("{}", cx).unwrap(); this } /// Updates the value of a setting in the user's global configuration. /// /// This is only for tests. Normally, settings are only loaded from /// JSON files. #[cfg(any(test, feature = "test-support"))] pub fn update_user_settings<T: Settings>( &mut self, cx: &mut App, update: impl FnOnce(&mut T::FileContent), ) { let old_text = serde_json::to_string(&self.raw_user_settings).unwrap(); let new_text = self.new_text_for_update::<T>(old_text, update); self.set_user_settings(&new_text, cx).unwrap(); } pub async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> { match fs.load(paths::settings_file()).await { result @ Ok(_) => result, Err(err) => { if let Some(e) = err.downcast_ref::<std::io::Error>() { if e.kind() == std::io::ErrorKind::NotFound { return Ok(crate::initial_user_settings_content().to_string()); } } Err(err) } } } pub async fn load_global_settings(fs: &Arc<dyn Fs>) -> Result<String> { match fs.load(paths::global_settings_file()).await { result @ Ok(_) => result, Err(err) => { if let Some(e) = err.downcast_ref::<std::io::Error>() { if e.kind() == std::io::ErrorKind::NotFound { return Ok("{}".to_string()); } } Err(err) } } } pub fn update_settings_file<T: Settings>( &self, fs: Arc<dyn Fs>, update: impl 'static + Send + FnOnce(&mut T::FileContent, &App), ) { self.setting_file_updates_tx .unbounded_send(Box::new(move |cx: AsyncApp| { async move { let old_text = Self::load_settings(&fs).await?; let new_text = cx.read_global(|store: &SettingsStore, cx| { store.new_text_for_update::<T>(old_text, |content| update(content, cx)) })?; let settings_path = paths::settings_file().as_path(); if fs.is_file(settings_path).await { let resolved_path = fs.canonicalize(settings_path).await.with_context(|| { format!("Failed to canonicalize settings path {:?}", settings_path) })?; fs.atomic_write(resolved_path.clone(), new_text) .await .with_context(|| { format!("Failed to write settings to file {:?}", resolved_path) })?; } else { fs.atomic_write(settings_path.to_path_buf(), new_text) .await .with_context(|| {