Compare commits

...

2 Commits

Author SHA1 Message Date
Anthony
1c567baa8e More proof of concept 2025-10-21 02:58:56 -04:00
Anthony
f623ea55b0 Basic proof of concept 2025-10-21 02:50:33 -04:00
4 changed files with 143 additions and 1 deletions

1
Cargo.lock generated
View File

@@ -15294,6 +15294,7 @@ dependencies = [
"schemars 1.0.4",
"search",
"serde",
"serde_json",
"session",
"settings",
"strum 0.27.2",

View File

@@ -30,6 +30,7 @@ project.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
strum.workspace = true
theme.workspace = true
@@ -51,5 +52,6 @@ node_runtime.workspace = true
paths.workspace = true
session.workspace = true
settings.workspace = true
workspace = { workspace = true, features = ["test-support"] }
zlog.workspace = true
pretty_assertions.workspace = true

View File

@@ -222,6 +222,85 @@ struct SettingFieldRenderer {
impl Global for SettingFieldRenderer {}
#[derive(Default, Clone)]
struct SettingsJsonKeyFinder {
key_finders: Rc<RefCell<HashMap<TypeId, Box<dyn Fn(&dyn AnySettingField) -> String>>>>,
}
impl Global for SettingsJsonKeyFinder {}
impl SettingsJsonKeyFinder {
fn add_basic_json_key_finder<T: 'static + Default + serde::Serialize>(&mut self) -> &mut Self {
let key = TypeId::of::<T>();
let key_finder = Box::new(move |any_field: &dyn AnySettingField| {
let field = *any_field
.as_any()
.downcast_ref::<SettingField<T>>()
.unwrap();
let mut content = SettingsContent::default();
// Write the field with a default value
(field.write)(&mut content, Some(T::default()));
// Serialize to a pretty-printed JSON string
let json_string = serde_json::to_string_pretty(&content)
.expect("Failed to serialize SettingsContent");
// Extract the JSON path from the string (should only have one entry)
extract_json_path_from_string(&json_string).unwrap()
});
self.key_finders.borrow_mut().insert(key, key_finder);
self
}
#[allow(dead_code)]
fn find_json_path(&self, field: &dyn AnySettingField) -> String {
let key = field.type_id();
let key_finders = self.key_finders.borrow();
let key_finder = key_finders.get(&key).unwrap();
key_finder(field)
}
}
/// Extracts the JSON key path from a serialized settings string.
/// Assumes only one field is set in the JSON.
fn extract_json_path_from_string(json: &str) -> Option<String> {
use serde_json::Value;
let value: Value = serde_json::from_str(json).ok()?;
fn find_non_empty_path(value: &Value, prefix: &str) -> Option<String> {
match value {
Value::Object(map) => {
for (key, val) in map {
let new_prefix = if prefix.is_empty() {
key.clone()
} else {
format!("{}.{}", prefix, key)
};
match val {
Value::Null => continue,
Value::Object(inner_map) if inner_map.is_empty() => continue,
Value::Array(arr) if arr.is_empty() => continue,
Value::Object(_) => {
if let Some(path) = find_non_empty_path(val, &new_prefix) {
return Some(path);
}
}
_ => return Some(new_prefix),
}
}
None
}
_ => None,
}
}
find_non_empty_path(&value, "")
}
impl SettingFieldRenderer {
fn add_basic_renderer<T: 'static>(
&mut self,
@@ -343,6 +422,7 @@ impl FeatureFlag for SettingsUiFeatureFlag {
pub fn init(cx: &mut App) {
init_renderers(cx);
init_json_key_finders(cx);
cx.observe_new(|workspace: &mut workspace::Workspace, _, _| {
workspace.register_action(|workspace, _: &OpenSettings, window, cx| {
@@ -356,6 +436,11 @@ pub fn init(cx: &mut App) {
.detach();
}
fn init_json_key_finders(cx: &mut App) {
cx.default_global::<SettingsJsonKeyFinder>()
.add_basic_json_key_finder::<bool>();
}
fn init_renderers(cx: &mut App) {
cx.default_global::<SettingFieldRenderer>()
.add_basic_renderer::<UnimplementedSettingField>(|_, _, _, _, _| {
@@ -3114,8 +3199,29 @@ fn render_icon_theme_picker(
#[cfg(test)]
mod test {
use gpui::TestAppContext;
use workspace::AppState;
use super::*;
#[gpui::test]
fn test_json_path(cx: &mut TestAppContext) {
cx.update(|cx| {
register_settings(cx);
init_json_key_finders(cx);
let bool_field = SettingField::<bool> {
pick: |content| content.vim_mode.as_ref(),
write: |content, value| content.vim_mode = value,
};
let json_key_finder = cx.global::<SettingsJsonKeyFinder>();
let path = json_key_finder.find_json_path(&bool_field);
assert_eq!(path, "vim_mode".to_string());
});
}
impl SettingsWindow {
fn navbar_entry(&self) -> usize {
self.navbar_entry
@@ -3134,9 +3240,14 @@ mod test {
}
fn register_settings(cx: &mut App) {
let app_state = workspace::AppState::test(cx);
settings::init(cx);
theme::init(theme::LoadThemes::JustBase, cx);
workspace::init_settings(cx);
// workspace::init_settings(cx);
workspace::init(app_state.clone(), cx);
AppState::set_global(Arc::downgrade(&app_state), cx);
project::Project::init_settings(cx);
language::init(cx);
editor::init(cx);

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<!-- <key>com.apple.security.cs.disable-library-validation</key>
<true/> -->
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
<true/>
<key>com.apple.security.personal-information.calendars</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
</dict>
</plist>