| @@ -222,6 +222,81 @@ struct SettingFieldRenderer { · 221 unmodified lines |
| 222 | | 222 | |
| 223 | impl Global for SettingFieldRenderer {} | 223 | impl Global for SettingFieldRenderer {} |
| 224 | | 224 | |
| | 225 | #[derive(Default, Clone)] |
| | 226 | struct SettingsJsonKeyFinder { |
| | 227 | key_finders: Rc<RefCell<HashMap<TypeId, Box<dyn Fn(SettingsContent) -> Option<String>>>>>, |
| | 228 | } |
| | 229 | |
| | 230 | impl Global for SettingsJsonKeyFinder {} |
| | 231 | |
| | 232 | impl SettingsJsonKeyFinder { |
| | 233 | fn add_basic_json_key_finder<T: 'static + Default + serde::Serialize>( |
| | 234 | &mut self, |
| | 235 | field: SettingField<T>, |
| | 236 | ) -> &mut Self { |
| | 237 | let key = TypeId::of::<T>(); |
| | 238 | let key_finder = Box::new(move |mut content: SettingsContent| { |
| | 239 | // Write the field with a default value |
| | 240 | (field.write)(&mut content, Some(T::default())); |
| | 241 | |
| | 242 | // Serialize to a pretty-printed JSON string |
| | 243 | let json_string = serde_json::to_string_pretty(&content) |
| | 244 | .expect("Failed to serialize SettingsContent"); |
| | 245 | |
| | 246 | // Extract the JSON path from the string (should only have one entry) |
| | 247 | extract_json_path_from_string(&json_string) |
| | 248 | }); |
| | 249 | self.key_finders.borrow_mut().insert(key, key_finder); |
| | 250 | self |
| | 251 | } |
| | 252 | |
| | 253 | fn find_json_path<T: 'static>(&self) -> Option<String> { |
| | 254 | let key = TypeId::of::<T>(); |
| | 255 | let key_finders = self.key_finders.borrow(); |
| | 256 | let key_finder = key_finders.get(&key)?; |
| | 257 | let content = SettingsContent::default(); |
| | 258 | key_finder(content) |
| | 259 | } |
| | 260 | } |
| | 261 | |
| | 262 | /// Extracts the JSON key path from a serialized settings string. |
| | 263 | /// Assumes only one field is set in the JSON. |
| | 264 | fn extract_json_path_from_string(json: &str) -> Option<String> { |
| | 265 | use serde_json::Value; |
| | 266 | |
| | 267 | let value: Value = serde_json::from_str(json).ok()?; |
| | 268 | |
| | 269 | fn find_non_empty_path(value: &Value, prefix: &str) -> Option<String> { |
| | 270 | match value { |
| | 271 | Value::Object(map) => { |
| | 272 | for (key, val) in map { |
| | 273 | let new_prefix = if prefix.is_empty() { |
| | 274 | key.clone() |
| | 275 | } else { |
| | 276 | format!("{}.{}", prefix, key) |
| | 277 | }; |
| | 278 | |
| | 279 | match val { |
| | 280 | Value::Null => continue, |
| | 281 | Value::Object(inner_map) if inner_map.is_empty() => continue, |
| | 282 | Value::Array(arr) if arr.is_empty() => continue, |
| | 283 | Value::Object(_) => { |
| | 284 | if let Some(path) = find_non_empty_path(val, &new_prefix) { |
| | 285 | return Some(path); |
| | 286 | } |
| | 287 | } |
| | 288 | _ => return Some(new_prefix), |
| | 289 | } |
| | 290 | } |
| | 291 | None |
| | 292 | } |
| | 293 | _ => None, |
| | 294 | } |
| | 295 | } |
| | 296 | |
| | 297 | find_non_empty_path(&value, "") |
| | 298 | } |
| | 299 | |
| 225 | impl SettingFieldRenderer { | 300 | impl SettingFieldRenderer { |
| 226 | fn add_basic_renderer<T: 'static>( | 301 | fn add_basic_renderer<T: 'static>( |
| 227 | &mut self, | 302 | &mut self, |
| @@ -343,6 +418,7 @@ impl FeatureFlag for SettingsUiFeatureFlag { · 115 unmodified lines |
| 343 | | 418 | |
| 344 | pub fn init(cx: &mut App) { | 419 | pub fn init(cx: &mut App) { |
| 345 | init_renderers(cx); | 420 | init_renderers(cx); |
| | 421 | init_json_key_finders(cx); |
| 346 | | 422 | |
| 347 | cx.observe_new(|workspace: &mut workspace::Workspace, _, _| { | 423 | cx.observe_new(|workspace: &mut workspace::Workspace, _, _| { |
| 348 | workspace.register_action(|workspace, _: &OpenSettings, window, cx| { | 424 | workspace.register_action(|workspace, _: &OpenSettings, window, cx| { |
| @@ -356,6 +432,14 @@ pub fn init(cx: &mut App) { · 7 unmodified lines |
| 356 | .detach(); | 432 | .detach(); |
| 357 | } | 433 | } |
| 358 | | 434 | |
| | 435 | fn init_json_key_finders(cx: &mut App) { |
| | 436 | cx.default_global::<SettingsJsonKeyFinder>() |
| | 437 | .add_basic_json_key_finder(SettingField::<bool> { |
| | 438 | pick: |content| content.vim_mode.as_ref(), |
| | 439 | write: |content, value| content.vim_mode = value, |
| | 440 | }); |
| | 441 | } |
| | 442 | |
| 359 | fn init_renderers(cx: &mut App) { | 443 | fn init_renderers(cx: &mut App) { |
| 360 | cx.default_global::<SettingFieldRenderer>() | 444 | cx.default_global::<SettingFieldRenderer>() |
| 361 | .add_basic_renderer::<UnimplementedSettingField>(|_, _, _, _, _| { | 445 | .add_basic_renderer::<UnimplementedSettingField>(|_, _, _, _, _| { |
| @@ -3114,8 +3198,25 @@ fn render_icon_theme_picker( · 2752 unmodified lines |
| 3114 | #[cfg(test)] | 3198 | #[cfg(test)] |
| 3115 | mod test { | 3199 | mod test { |
| 3116 | | 3200 | |
| | 3201 | use gpui::TestAppContext; |
| | 3202 | use workspace::AppState; |
| | 3203 | |
| 3117 | use super::*; | 3204 | use super::*; |
| 3118 | | 3205 | |
| | 3206 | #[gpui::test] |
| | 3207 | fn test_json_path(cx: &mut TestAppContext) { |
| | 3208 | cx.update(|cx| { |
| | 3209 | register_settings(cx); |
| | 3210 | init_json_key_finders(cx); |
| | 3211 | |
| | 3212 | let json_key_finder = cx.global::<SettingsJsonKeyFinder>(); |
| | 3213 | let path = json_key_finder.find_json_path::<bool>(); |
| | 3214 | dbg!(&path); |
| | 3215 | |
| | 3216 | assert_eq!(path, Some("vim_mode".to_string())); |
| | 3217 | }); |
| | 3218 | } |
| | 3219 | |
| 3119 | impl SettingsWindow { | 3220 | impl SettingsWindow { |
| 3120 | fn navbar_entry(&self) -> usize { | 3221 | fn navbar_entry(&self) -> usize { |
| 3121 | self.navbar_entry | 3222 | self.navbar_entry |
| @@ -3134,9 +3235,14 @@ mod test { · 12 unmodified lines |
| 3134 | } | 3235 | } |
| 3135 | | 3236 | |
| 3136 | fn register_settings(cx: &mut App) { | 3237 | fn register_settings(cx: &mut App) { |
| | 3238 | let app_state = workspace::AppState::test(cx); |
| | 3239 | |
| 3137 | settings::init(cx); | 3240 | settings::init(cx); |
| 3138 | theme::init(theme::LoadThemes::JustBase, cx); | 3241 | theme::init(theme::LoadThemes::JustBase, cx); |
| 3139 | workspace::init_settings(cx); | 3242 | // workspace::init_settings(cx); |
| | 3243 | workspace::init(app_state.clone(), cx); |
| | 3244 | AppState::set_global(Arc::downgrade(&app_state), cx); |
| | 3245 | |
| 3140 | project::Project::init_settings(cx); | 3246 | project::Project::init_settings(cx); |
| 3141 | language::init(cx); | 3247 | language::init(cx); |
| 3142 | editor::init(cx); | 3248 | editor::init(cx); |