Implement global settings file (#30444)
Adds a `global_settings.json` file which can be set up by enterprises with automation, enabling setting settings like edit provider by default without interfering with user's settings files. Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
parent
c6e69fae17
commit
90c2d17042
4 changed files with 210 additions and 40 deletions
|
@ -120,6 +120,8 @@ pub trait Settings: 'static + Send + Sync {
|
|||
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.
|
||||
|
@ -140,8 +142,9 @@ impl<'a, T: Serialize> SettingsSources<'a, T> {
|
|||
|
||||
/// Returns an iterator over all of the settings customizations.
|
||||
pub fn customizations(&self) -> impl Iterator<Item = &T> {
|
||||
self.extensions
|
||||
self.global
|
||||
.into_iter()
|
||||
.chain(self.extensions)
|
||||
.chain(self.user)
|
||||
.chain(self.release_channel)
|
||||
.chain(self.server)
|
||||
|
@ -180,6 +183,7 @@ pub struct SettingsLocation<'a> {
|
|||
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,
|
||||
|
@ -272,6 +276,7 @@ impl SettingsStore {
|
|||
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!({}),
|
||||
|
@ -341,6 +346,7 @@ impl SettingsStore {
|
|||
.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(),
|
||||
|
@ -388,6 +394,11 @@ impl SettingsStore {
|
|||
&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);
|
||||
|
@ -426,6 +437,20 @@ impl SettingsStore {
|
|||
}
|
||||
}
|
||||
|
||||
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>,
|
||||
|
@ -637,6 +662,24 @@ impl SettingsStore {
|
|||
Ok(settings)
|
||||
}
|
||||
|
||||
/// Sets the global settings via a JSON string.
|
||||
pub fn set_global_settings(
|
||||
&mut self,
|
||||
global_settings_content: &str,
|
||||
cx: &mut App,
|
||||
) -> Result<Value> {
|
||||
let settings: Value = if global_settings_content.is_empty() {
|
||||
parse_json_with_comments("{}")?
|
||||
} else {
|
||||
parse_json_with_comments(global_settings_content)?
|
||||
};
|
||||
|
||||
anyhow::ensure!(settings.is_object(), "settings must be an object");
|
||||
self.raw_global_settings = Some(settings.clone());
|
||||
self.recompute_values(None, cx)?;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
pub fn set_server_settings(
|
||||
&mut self,
|
||||
server_settings_content: &str,
|
||||
|
@ -935,6 +978,11 @@ impl SettingsStore {
|
|||
message: e.to_string(),
|
||||
})?;
|
||||
|
||||
let global_settings = self
|
||||
.raw_global_settings
|
||||
.as_ref()
|
||||
.and_then(|setting| setting_value.deserialize_setting(setting).log_err());
|
||||
|
||||
let extension_settings = setting_value
|
||||
.deserialize_setting(&self.raw_extension_settings)
|
||||
.log_err();
|
||||
|
@ -972,6 +1020,7 @@ impl SettingsStore {
|
|||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
global: global_settings.as_ref(),
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
|
@ -1023,6 +1072,7 @@ impl SettingsStore {
|
|||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
global: global_settings.as_ref(),
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
|
@ -1139,6 +1189,9 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
|
|||
Ok(Box::new(T::load(
|
||||
SettingsSources {
|
||||
default: values.default.0.downcast_ref::<T::FileContent>().unwrap(),
|
||||
global: values
|
||||
.global
|
||||
.map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
|
||||
extensions: values
|
||||
.extensions
|
||||
.map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
|
||||
|
@ -2072,6 +2125,70 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_global_settings(cx: &mut App) {
|
||||
let mut store = SettingsStore::new(cx);
|
||||
store.register_setting::<UserSettings>(cx);
|
||||
store
|
||||
.set_default_settings(
|
||||
r#"{
|
||||
"user": {
|
||||
"name": "John Doe",
|
||||
"age": 30,
|
||||
"staff": false
|
||||
}
|
||||
}"#,
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Set global settings - these should override defaults but not user settings
|
||||
store
|
||||
.set_global_settings(
|
||||
r#"{
|
||||
"user": {
|
||||
"name": "Global User",
|
||||
"age": 35,
|
||||
"staff": true
|
||||
}
|
||||
}"#,
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Before user settings, global settings should apply
|
||||
assert_eq!(
|
||||
store.get::<UserSettings>(None),
|
||||
&UserSettings {
|
||||
name: "Global User".to_string(),
|
||||
age: 35,
|
||||
staff: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Set user settings - these should override both defaults and global
|
||||
store
|
||||
.set_user_settings(
|
||||
r#"{
|
||||
"user": {
|
||||
"age": 40
|
||||
}
|
||||
}"#,
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// User settings should override global settings
|
||||
assert_eq!(
|
||||
store.get::<UserSettings>(None),
|
||||
&UserSettings {
|
||||
name: "Global User".to_string(), // Name from global settings
|
||||
age: 40, // Age from user settings
|
||||
staff: true, // Staff from global settings
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
struct LanguageSettings {
|
||||
#[serde(default)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue