Make settings store handle no user settings (#2550)

This fixes the crash users have been reporting with the theme selector
This commit is contained in:
Mikayla Maki 2023-06-01 10:25:30 -07:00 committed by GitHub
commit d64dc3960d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,4 +1,4 @@
use anyhow::Result; use anyhow::{anyhow, Result};
use collections::{btree_map, hash_map, BTreeMap, HashMap}; use collections::{btree_map, hash_map, BTreeMap, HashMap};
use gpui::AppContext; use gpui::AppContext;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -84,15 +84,26 @@ pub struct SettingsJsonSchemaParams<'a> {
} }
/// A set of strongly-typed setting values defined via multiple JSON files. /// A set of strongly-typed setting values defined via multiple JSON files.
#[derive(Default)]
pub struct SettingsStore { pub struct SettingsStore {
setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>, setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
default_deserialized_settings: Option<serde_json::Value>, default_deserialized_settings: serde_json::Value,
user_deserialized_settings: Option<serde_json::Value>, user_deserialized_settings: serde_json::Value,
local_deserialized_settings: BTreeMap<(usize, Arc<Path>), serde_json::Value>, local_deserialized_settings: BTreeMap<(usize, Arc<Path>), serde_json::Value>,
tab_size_callback: Option<(TypeId, Box<dyn Fn(&dyn Any) -> Option<usize>>)>, tab_size_callback: Option<(TypeId, Box<dyn Fn(&dyn Any) -> Option<usize>>)>,
} }
impl Default for SettingsStore {
fn default() -> Self {
SettingsStore {
setting_values: Default::default(),
default_deserialized_settings: serde_json::json!({}),
user_deserialized_settings: serde_json::json!({}),
local_deserialized_settings: Default::default(),
tab_size_callback: Default::default(),
}
}
}
#[derive(Debug)] #[derive(Debug)]
struct SettingValue<T> { struct SettingValue<T> {
global_value: Option<T>, global_value: Option<T>,
@ -136,20 +147,18 @@ impl SettingsStore {
local_values: Vec::new(), local_values: Vec::new(),
})); }));
if let Some(default_settings) = &self.default_deserialized_settings {
if let Some(default_settings) = setting_value if let Some(default_settings) = setting_value
.deserialize_setting(default_settings) .deserialize_setting(&self.default_deserialized_settings)
.log_err() .log_err()
{ {
let mut user_values_stack = Vec::new(); let mut user_values_stack = Vec::new();
if let Some(user_settings) = &self.user_deserialized_settings { if let Some(user_settings) = setting_value
if let Some(user_settings) = .deserialize_setting(&self.user_deserialized_settings)
setting_value.deserialize_setting(user_settings).log_err() .log_err()
{ {
user_values_stack = vec![user_settings]; user_values_stack = vec![user_settings];
} }
}
if let Some(setting) = setting_value if let Some(setting) = setting_value
.load_setting(&default_settings, &user_values_stack, cx) .load_setting(&default_settings, &user_values_stack, cx)
@ -159,7 +168,6 @@ impl SettingsStore {
} }
} }
} }
}
/// Get the value of a setting. /// Get the value of a setting.
/// ///
@ -189,9 +197,7 @@ impl SettingsStore {
/// This is only for debugging and reporting. For user-facing functionality, /// This is only for debugging and reporting. For user-facing functionality,
/// use the typed setting interface. /// use the typed setting interface.
pub fn untyped_user_settings(&self) -> &serde_json::Value { pub fn untyped_user_settings(&self) -> &serde_json::Value {
self.user_deserialized_settings &self.user_deserialized_settings
.as_ref()
.unwrap_or(&serde_json::Value::Null)
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
@ -213,11 +219,7 @@ impl SettingsStore {
cx: &AppContext, cx: &AppContext,
update: impl FnOnce(&mut T::FileContent), update: impl FnOnce(&mut T::FileContent),
) { ) {
if self.user_deserialized_settings.is_none() { let old_text = serde_json::to_string(&self.user_deserialized_settings).unwrap();
self.set_user_settings("{}", cx).unwrap();
}
let old_text =
serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap();
let new_text = self.new_text_for_update::<T>(old_text, update); let new_text = self.new_text_for_update::<T>(old_text, update);
self.set_user_settings(&new_text, cx).unwrap(); self.set_user_settings(&new_text, cx).unwrap();
} }
@ -250,11 +252,7 @@ impl SettingsStore {
.setting_values .setting_values
.get(&setting_type_id) .get(&setting_type_id)
.unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>())) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
.deserialize_setting( .deserialize_setting(&self.user_deserialized_settings)
self.user_deserialized_settings
.as_ref()
.expect("no user settings loaded"),
)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
panic!( panic!(
"could not deserialize setting type {} from user settings: {}", "could not deserialize setting type {} from user settings: {}",
@ -323,10 +321,14 @@ impl SettingsStore {
default_settings_content: &str, default_settings_content: &str,
cx: &AppContext, cx: &AppContext,
) -> Result<()> { ) -> Result<()> {
self.default_deserialized_settings = let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?;
Some(parse_json_with_comments(default_settings_content)?); if settings.is_object() {
self.default_deserialized_settings = settings;
self.recompute_values(None, cx)?; self.recompute_values(None, cx)?;
Ok(()) Ok(())
} else {
Err(anyhow!("settings must be an object"))
}
} }
/// Set the user settings via a JSON string. /// Set the user settings via a JSON string.
@ -335,9 +337,14 @@ impl SettingsStore {
user_settings_content: &str, user_settings_content: &str,
cx: &AppContext, cx: &AppContext,
) -> Result<()> { ) -> Result<()> {
self.user_deserialized_settings = Some(parse_json_with_comments(user_settings_content)?); let settings: serde_json::Value = parse_json_with_comments(user_settings_content)?;
if settings.is_object() {
self.user_deserialized_settings = settings;
self.recompute_values(None, cx)?; self.recompute_values(None, cx)?;
Ok(()) Ok(())
} else {
Err(anyhow!("settings must be an object"))
}
} }
/// Add or remove a set of local settings via a JSON string. /// Add or remove a set of local settings via a JSON string.
@ -361,7 +368,6 @@ impl SettingsStore {
/// Add or remove a set of local settings via a JSON string. /// Add or remove a set of local settings via a JSON string.
pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> { pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> {
eprintln!("clearing local settings {root_id}");
self.local_deserialized_settings self.local_deserialized_settings
.retain(|k, _| k.0 != root_id); .retain(|k, _| k.0 != root_id);
self.recompute_values(Some((root_id, "".as_ref())), cx)?; self.recompute_values(Some((root_id, "".as_ref())), cx)?;
@ -460,20 +466,19 @@ impl SettingsStore {
let mut user_settings_stack = Vec::<DeserializedSetting>::new(); let mut user_settings_stack = Vec::<DeserializedSetting>::new();
let mut paths_stack = Vec::<Option<(usize, &Path)>>::new(); let mut paths_stack = Vec::<Option<(usize, &Path)>>::new();
for setting_value in self.setting_values.values_mut() { for setting_value in self.setting_values.values_mut() {
if let Some(default_settings) = &self.default_deserialized_settings { let default_settings =
let default_settings = setting_value.deserialize_setting(default_settings)?; setting_value.deserialize_setting(&self.default_deserialized_settings)?;
user_settings_stack.clear(); user_settings_stack.clear();
paths_stack.clear(); paths_stack.clear();
if let Some(user_settings) = &self.user_deserialized_settings { if let Some(user_settings) = setting_value
if let Some(user_settings) = .deserialize_setting(&self.user_deserialized_settings)
setting_value.deserialize_setting(user_settings).log_err() .log_err()
{ {
user_settings_stack.push(user_settings); user_settings_stack.push(user_settings);
paths_stack.push(None); paths_stack.push(None);
} }
}
// If the global settings file changed, reload the global value for the field. // If the global settings file changed, reload the global value for the field.
if changed_local_path.is_none() { if changed_local_path.is_none() {
@ -507,12 +512,9 @@ impl SettingsStore {
// If a local settings file changed, then avoid recomputing local // If a local settings file changed, then avoid recomputing local
// settings for any path outside of that directory. // settings for any path outside of that directory.
if changed_local_path.map_or( if changed_local_path.map_or(false, |(changed_root_id, changed_local_path)| {
false,
|(changed_root_id, changed_local_path)| {
*root_id != changed_root_id || !path.starts_with(changed_local_path) *root_id != changed_root_id || !path.starts_with(changed_local_path)
}, }) {
) {
continue; continue;
} }
@ -525,7 +527,6 @@ impl SettingsStore {
} }
} }
} }
}
Ok(()) Ok(())
} }
} }