mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-05 02:20:10 +00:00
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:
commit
d64dc3960d
1 changed files with 97 additions and 96 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue