settings: Improve parse errors (#37234)

Ben Kunkle created

Closes #ISSUE

Adds a dependency on `serde_path_to_error` to the workspace allowing us
to include the path to the setting that failed to parse on settings
parse failure.

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

Cargo.lock                            |  1 +
Cargo.toml                            |  1 +
crates/settings/Cargo.toml            |  1 +
crates/settings/src/settings_json.rs  |  3 ++-
crates/settings/src/settings_store.rs | 26 +++++++++++++++++++++++---
docs/src/visual-customization.md      |  2 +-
6 files changed, 29 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -14894,6 +14894,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "serde_json_lenient",
+ "serde_path_to_error",
  "settings_ui_macros",
  "smallvec",
  "tree-sitter",

Cargo.toml 🔗

@@ -592,6 +592,7 @@ serde_json_lenient = { version = "0.2", features = [
     "preserve_order",
     "raw_value",
 ] }
+serde_path_to_error = "0.1.17"
 serde_repr = "0.1"
 serde_urlencoded = "0.7"
 sha2 = "0.10"

crates/settings/Cargo.toml 🔗

@@ -33,6 +33,7 @@ serde_derive.workspace = true
 serde_json.workspace = true
 settings_ui_macros.workspace = true
 serde_json_lenient.workspace = true
+serde_path_to_error.workspace = true
 smallvec.workspace = true
 tree-sitter-json.workspace = true
 tree-sitter.workspace = true

crates/settings/src/settings_json.rs 🔗

@@ -563,7 +563,8 @@ pub fn to_pretty_json(
 }
 
 pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T> {
-    Ok(serde_json_lenient::from_str(content)?)
+    let mut deserializer = serde_json_lenient::Deserializer::from_str(content);
+    Ok(serde_path_to_error::deserialize(&mut deserializer)?)
 }
 
 #[cfg(test)]

crates/settings/src/settings_store.rs 🔗

@@ -11,7 +11,7 @@ use gpui::{App, AsyncApp, BorrowAppContext, Global, SharedString, Task, UpdateGl
 
 use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};
 use schemars::JsonSchema;
-use serde::{Deserialize, Serialize, de::DeserializeOwned};
+use serde::{Serialize, de::DeserializeOwned};
 use serde_json::{Value, json};
 use smallvec::SmallVec;
 use std::{
@@ -1464,9 +1464,29 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
                 return (T::KEY, Ok(DeserializedSetting(Box::new(value))));
             }
         }
-        let value = T::FileContent::deserialize(json)
+        let value = serde_path_to_error::deserialize::<_, T::FileContent>(json)
             .map(|value| DeserializedSetting(Box::new(value)))
-            .map_err(anyhow::Error::from);
+            .map_err(|err| {
+                // construct a path using the key and reported error path if possible.
+                // Unfortunately, serde_path_to_error does not expose the necessary
+                // methods and data to simply add the key to the path
+                let mut path = String::new();
+                if let Some(key) = key {
+                    path.push_str(key);
+                }
+                let err_path = err.path().to_string();
+                // when the path is empty, serde_path_to_error stringifies the path as ".",
+                // when the path is unknown, serde_path_to_error stringifies the path as an empty string
+                if !err_path.is_empty() && !err_path.starts_with(".") {
+                    path.push('.');
+                    path.push_str(&err_path);
+                }
+                if path.is_empty() {
+                    anyhow::Error::from(err.into_inner())
+                } else {
+                    anyhow::anyhow!("'{}': {}", err.into_inner(), path)
+                }
+            });
         (key, value)
     }
 

docs/src/visual-customization.md 🔗

@@ -8,7 +8,7 @@ See [Configuring Zed](./configuring-zed.md) for additional information and other
 
 Use may install zed extensions providing [Themes](./themes.md) and [Icon Themes](./icon-themes.md) via {#action zed::Extensions} from the command palette or menu.
 
-You can preview/choose amongsts your installed themes and icon themes with {#action theme_selector::Toggle} ({#kb theme_selector::Toggle}) and ({#action icon_theme_selector::Toggle}) which will modify the following settings:
+You can preview/choose amongst your installed themes and icon themes with {#action theme_selector::Toggle} ({#kb theme_selector::Toggle}) and ({#action icon_theme_selector::Toggle}) which will modify the following settings:
 
 ```json
 {