From 43061b6b16f0e3fdf660a298b5a1edd3c781e805 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 Sep 2025 10:08:06 -0700 Subject: [PATCH] Add SettingsFile APIs to SettingsStore (#39129) Closes #ISSUE Adds a couple functions to the `SettingsStore`: - `get_value_from_file`: Gets a value from a given settings file (`Local`, `User`, etc) and if the value isn't found in the requested file, walks the known settings files in the order in which they are merged to find the settings value in lower precedence settings files (i.e. if value not set anywhere will always return default value) - `get_overrides_for_field`: Returns a list of settings files where a given setting is set that have higher precedence than the passed in file. e.g. passing in user will result in project settings files where the value is set being returned. Additionally changes the default for the `project_name` setting to uphold the rules we are attempting to enforce on the settings, namely: - All settings fields should be of the form `Option` - `None` (or `null` in JSON) should never be a meaningful value Follow up PRs will handle implementing a function to write to an arbitrary settings file, and passing through metadata to the above functions to control how overrides are determined for more complicated cases like `SaturatingBool` (`disable_ai`) and `ExtendingVec` Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: Ben Kunkle Co-authored-by: Anthony Eid Co-authored-by: Danilo Leal Co-authored-by: Anthony --- assets/settings/default.json | 4 +- .../settings/src/settings_content/project.rs | 4 +- crates/settings/src/settings_store.rs | 406 +++++++++++++++++- crates/settings_ui/src/settings_ui.rs | 183 ++++---- crates/worktree/src/worktree_settings.rs | 2 +- 5 files changed, 506 insertions(+), 93 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 4585433a69d539dd63a2d19f3c3f1f17c5b84b5d..21cd3b84041b89344516145dbdfe79151199bf65 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1,5 +1,7 @@ { - "project_name": null, + /// The displayed name of this project. If not set or empty, the root directory name + /// will be displayed. + "project_name": "", // The name of the Zed theme to use for the UI. // // `mode` is one of: diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs index cc258148e396bb1d4ebf879423f1900b84e082c3..dce2a39d32d7b570407c209668bebf56d6c34704 100644 --- a/crates/settings/src/settings_content/project.rs +++ b/crates/settings/src/settings_content/project.rs @@ -50,10 +50,10 @@ pub struct ProjectSettingsContent { #[skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)] pub struct WorktreeSettingsContent { - /// The displayed name of this project. If not set, the root directory name + /// The displayed name of this project. If not set or empty, the root directory name /// will be displayed. /// - /// Default: none + /// Default: "" pub project_name: Option, /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index e959756bac8aaaddd01a49b82e2dcf0ee85d1a1f..b94a24866c76a57328f3b1ebf6a688b2fa4b3820 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -157,13 +157,12 @@ pub struct SettingsStore { mpsc::UnboundedSender LocalBoxFuture<'static, Result<()>>>>, } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub enum SettingsFile { User, - Global, - Extension, Server, Default, + /// Local also represents project settings in ssh projects as well as local projects Local((WorktreeId, Arc)), } @@ -479,19 +478,98 @@ impl SettingsStore { // ignoring profiles // ignoring os profiles // ignoring release channel profiles + // ignoring global + // ignoring extension if self.user_settings.is_some() { files.push(SettingsFile::User); } - if self.extension_settings.is_some() { - files.push(SettingsFile::Extension); - } - if self.global_settings.is_some() { - files.push(SettingsFile::Global); - } files.push(SettingsFile::Default); files } + + fn get_content_for_file(&self, file: SettingsFile) -> Option<&SettingsContent> { + match file { + SettingsFile::User => self + .user_settings + .as_ref() + .map(|settings| settings.content.as_ref()), + SettingsFile::Default => Some(self.default_settings.as_ref()), + SettingsFile::Server => self.server_settings.as_deref(), + SettingsFile::Local(ref key) => self.local_settings.get(key), + } + } + + pub fn get_overrides_for_field( + &self, + target_file: SettingsFile, + get: fn(&SettingsContent) -> &Option, + ) -> Vec { + let all_files = self.get_all_files(); + let mut found_file = false; + let mut overrides = Vec::new(); + + for file in all_files.into_iter().rev() { + if !found_file { + found_file = file == target_file; + continue; + } + + if let SettingsFile::Local((wt_id, ref path)) = file + && let SettingsFile::Local((target_wt_id, ref target_path)) = target_file + && (wt_id != target_wt_id || !target_path.starts_with(path)) + { + // if requesting value from a local file, don't return values from local files in different worktrees + continue; + } + + let Some(content) = self.get_content_for_file(file.clone()) else { + continue; + }; + if get(content).is_some() { + overrides.push(file); + } + } + + overrides + } + + pub fn get_value_from_file( + &self, + target_file: SettingsFile, + pick: fn(&SettingsContent) -> &Option, + ) -> (SettingsFile, &T) { + // TODO: Add a metadata field for overriding the "overrides" tag, for contextually different settings + // e.g. disable AI isn't overridden, or a vec that gets extended instead or some such + + // todo(settings_ui) cache all files + let all_files = self.get_all_files(); + let mut found_file = false; + + for file in all_files.into_iter() { + if !found_file && file != target_file && file != SettingsFile::Default { + continue; + } + found_file = true; + + if let SettingsFile::Local((wt_id, ref path)) = file + && let SettingsFile::Local((target_wt_id, ref target_path)) = target_file + && (wt_id != target_wt_id || !target_path.starts_with(&path)) + { + // if requesting value from a local file, don't return values from local files in different worktrees + continue; + } + + let Some(content) = self.get_content_for_file(file.clone()) else { + continue; + }; + if let Some(value) = pick(content).as_ref() { + return (file, value); + } + } + + unreachable!("All values should have defaults"); + } } impl SettingsStore { @@ -1609,4 +1687,314 @@ mod tests { } ); } + + #[gpui::test] + fn test_get_value_for_field_basic(cx: &mut App) { + let mut store = SettingsStore::new(cx, &test_settings()); + store.register_setting::(cx); + + store + .set_user_settings(r#"{"preferred_line_length": 0}"#, cx) + .unwrap(); + let local = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + store + .set_local_settings( + local.0, + local.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + + fn get(content: &SettingsContent) -> &Option { + &content.project.all_languages.defaults.preferred_line_length + } + + let default_value = get(&store.default_settings).unwrap(); + + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local.clone()), get), + (SettingsFile::User, &0) + ); + assert_eq!( + store.get_value_from_file(SettingsFile::User, get), + (SettingsFile::User, &0) + ); + store.set_user_settings(r#"{}"#, cx).unwrap(); + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local.clone()), get), + (SettingsFile::Default, &default_value) + ); + store + .set_local_settings( + local.0, + local.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 80}"#), + cx, + ) + .unwrap(); + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local.clone()), get), + (SettingsFile::Local(local), &80) + ); + assert_eq!( + store.get_value_from_file(SettingsFile::User, get), + (SettingsFile::Default, &default_value) + ); + } + + #[gpui::test] + fn test_get_value_for_field_local_worktrees_dont_interfere(cx: &mut App) { + let mut store = SettingsStore::new(cx, &test_settings()); + store.register_setting::(cx); + store.register_setting::(cx); + + let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + + let local_1_child = ( + WorktreeId::from_usize(0), + RelPath::new( + std::path::Path::new("child1"), + util::paths::PathStyle::Posix, + ) + .unwrap() + .into_arc(), + ); + + let local_2 = (WorktreeId::from_usize(1), RelPath::empty().into_arc()); + let local_2_child = ( + WorktreeId::from_usize(1), + RelPath::new( + std::path::Path::new("child2"), + util::paths::PathStyle::Posix, + ) + .unwrap() + .into_arc(), + ); + + fn get(content: &SettingsContent) -> &Option { + &content.project.all_languages.defaults.preferred_line_length + } + + store + .set_local_settings( + local_1.0, + local_1.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 1}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + local_1_child.0, + local_1_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + local_2.0, + local_2.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 2}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + local_2_child.0, + local_2_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + + // each local child should only inherit from it's parent + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local_2_child), get), + (SettingsFile::Local(local_2), &2) + ); + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local_1_child.clone()), get), + (SettingsFile::Local(local_1.clone()), &1) + ); + + // adjacent children should be treated as siblings not inherit from each other + let local_1_adjacent_child = (local_1.0, rel_path("adjacent_child").into_arc()); + store + .set_local_settings( + local_1_adjacent_child.0, + local_1_adjacent_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + local_1_child.0, + local_1_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 3}"#), + cx, + ) + .unwrap(); + + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local_1_adjacent_child.clone()), get), + (SettingsFile::Local(local_1.clone()), &1) + ); + store + .set_local_settings( + local_1_adjacent_child.0, + local_1_adjacent_child.1, + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 3}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + local_1_child.0, + local_1_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + assert_eq!( + store.get_value_from_file(SettingsFile::Local(local_1_child), get), + (SettingsFile::Local(local_1), &1) + ); + } + + #[gpui::test] + fn test_get_overrides_for_field(cx: &mut App) { + let mut store = SettingsStore::new(cx, &test_settings()); + store.register_setting::(cx); + + let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc()); + let wt0_child2 = (WorktreeId::from_usize(0), rel_path("child2").into_arc()); + + let wt1_root = (WorktreeId::from_usize(1), RelPath::empty().into_arc()); + let wt1_subdir = (WorktreeId::from_usize(1), rel_path("subdir").into_arc()); + + fn get(content: &SettingsContent) -> &Option { + &content.project.all_languages.defaults.preferred_line_length + } + + store + .set_user_settings(r#"{"preferred_line_length": 100}"#, cx) + .unwrap(); + + store + .set_local_settings( + wt0_root.0, + wt0_root.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 80}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + wt0_child1.0, + wt0_child1.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 120}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + wt0_child2.0, + wt0_child2.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + + store + .set_local_settings( + wt1_root.0, + wt1_root.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 90}"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + wt1_subdir.0, + wt1_subdir.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{}"#), + cx, + ) + .unwrap(); + + let overrides = store.get_overrides_for_field(SettingsFile::Default, get); + assert_eq!( + overrides, + vec![ + SettingsFile::User, + SettingsFile::Local(wt0_root.clone()), + SettingsFile::Local(wt0_child1.clone()), + SettingsFile::Local(wt1_root.clone()), + ] + ); + + let overrides = store.get_overrides_for_field(SettingsFile::User, get); + assert_eq!( + overrides, + vec![ + SettingsFile::Local(wt0_root.clone()), + SettingsFile::Local(wt0_child1.clone()), + SettingsFile::Local(wt1_root.clone()), + ] + ); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt0_root), get); + assert_eq!(overrides, vec![]); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt0_child1.clone()), get); + assert_eq!(overrides, vec![]); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt0_child2), get); + assert_eq!(overrides, vec![]); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt1_root), get); + assert_eq!(overrides, vec![]); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt1_subdir), get); + assert_eq!(overrides, vec![]); + + let wt0_deep_child = ( + WorktreeId::from_usize(0), + rel_path("child1/subdir").into_arc(), + ); + store + .set_local_settings( + wt0_deep_child.0, + wt0_deep_child.1.clone(), + LocalSettingsKind::Settings, + Some(r#"{"preferred_line_length": 140}"#), + cx, + ) + .unwrap(); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt0_deep_child), get); + assert_eq!(overrides, vec![]); + + let overrides = store.get_overrides_for_field(SettingsFile::Local(wt0_child1), get); + assert_eq!(overrides, vec![]); + } } diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index c5f9258998c7ca9a778e6046c741db7cba7f122b..d52e2eea8a09f99d7d57943cce84f02251a7bd5c 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -22,16 +22,17 @@ use util::{paths::PathStyle, rel_path::RelPath}; use crate::components::SettingsEditor; -#[derive(Clone)] +#[derive(Clone, Copy)] struct SettingField { - pick: fn(&SettingsContent) -> &T, - pick_mut: fn(&mut SettingsContent) -> &mut T, + pick: fn(&SettingsContent) -> &Option, + pick_mut: fn(&mut SettingsContent) -> &mut Option, } trait AnySettingField { fn as_any(&self) -> &dyn Any; fn type_name(&self) -> &'static str; fn type_id(&self) -> TypeId; + fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> settings::SettingsFile; } impl AnySettingField for SettingField { @@ -46,6 +47,13 @@ impl AnySettingField for SettingField { fn type_id(&self) -> TypeId { TypeId::of::() } + + fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> settings::SettingsFile { + let (file, _) = cx + .global::() + .get_value_from_file(file.to_settings(), self.pick); + return file; + } } #[derive(Default, Clone)] @@ -57,6 +65,7 @@ struct SettingFieldRenderer { Box< dyn Fn( &dyn AnySettingField, + SettingsUiFile, Option<&SettingsFieldMetadata>, &mut Window, &mut App, @@ -74,6 +83,7 @@ impl SettingFieldRenderer { &mut self, renderer: impl Fn( &SettingField, + SettingsUiFile, Option<&SettingsFieldMetadata>, &mut Window, &mut App, @@ -83,6 +93,7 @@ impl SettingFieldRenderer { let key = TypeId::of::(); let renderer = Box::new( move |any_setting_field: &dyn AnySettingField, + settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, window: &mut Window, cx: &mut App| { @@ -90,7 +101,7 @@ impl SettingFieldRenderer { .as_any() .downcast_ref::>() .unwrap(); - renderer(field, metadata, window, cx) + renderer(field, settings_file, metadata, window, cx) }, ); self.renderers.borrow_mut().insert(key, renderer); @@ -100,13 +111,14 @@ impl SettingFieldRenderer { fn render( &self, any_setting_field: &dyn AnySettingField, + settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, window: &mut Window, cx: &mut App, ) -> AnyElement { let key = any_setting_field.type_id(); if let Some(renderer) = self.renderers.borrow().get(&key) { - renderer(any_setting_field, metadata, window, cx) + renderer(any_setting_field, settings_file, metadata, window, cx) } else { panic!( "No renderer found for type: {}", @@ -269,18 +281,19 @@ pub fn init(cx: &mut App) { } fn init_renderers(cx: &mut App) { + // fn (field: SettingsField, current_file: SettingsFile, cx) -> (currently_set_in: SettingsFile, overridden_in: Vec) cx.default_global::() - .add_renderer::>(|settings_field, _, _, cx| { - render_toggle_button(settings_field.clone(), cx).into_any_element() + .add_renderer::(|settings_field, file, _, _, cx| { + render_toggle_button(*settings_field, file, cx).into_any_element() }) - .add_renderer::>(|settings_field, metadata, _, cx| { - render_text_field(settings_field.clone(), metadata, cx) + .add_renderer::(|settings_field, file, metadata, _, cx| { + render_text_field(settings_field.clone(), file, metadata, cx) }) - .add_renderer::>(|settings_field, _, _, cx| { - render_toggle_button(settings_field.clone(), cx) + .add_renderer::(|settings_field, file, _, _, cx| { + render_toggle_button(*settings_field, file, cx) }) - .add_renderer::>(|settings_field, _, window, cx| { - render_dropdown(settings_field.clone(), window, cx) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) }); } @@ -304,8 +317,8 @@ pub fn open_settings_editor(cx: &mut App) -> anyhow::Result, - current_file: SettingsFile, + files: Vec, + current_file: SettingsUiFile, pages: Vec, search: Entity, navbar_entry: usize, // Index into pages - should probably be (usize, Option) for section + page @@ -340,7 +353,7 @@ enum SettingsPageItem { } impl SettingsPageItem { - fn render(&self, _file: SettingsFile, window: &mut Window, cx: &mut App) -> AnyElement { + fn render(&self, file: SettingsUiFile, window: &mut Window, cx: &mut App) -> AnyElement { match self { SettingsPageItem::SectionHeader(header) => v_flex() .w_full() @@ -350,6 +363,9 @@ impl SettingsPageItem { .into_any_element(), SettingsPageItem::SettingItem(setting_item) => { let renderer = cx.default_global::().clone(); + let file_set_in = + SettingsUiFile::from_settings(setting_item.field.file_set_in(file.clone(), cx)); + h_flex() .id(setting_item.title) .w_full() @@ -361,8 +377,25 @@ impl SettingsPageItem { .max_w_1_2() .flex_shrink() .child( - Label::new(SharedString::new_static(setting_item.title)) - .size(LabelSize::Default), + h_flex() + .w_full() + .gap_4() + .child( + Label::new(SharedString::new_static(setting_item.title)) + .size(LabelSize::Default), + ) + .when_some( + file_set_in.filter(|file_set_in| file_set_in != &file), + |elem, file_set_in| { + elem.child( + Label::new(format!( + "set in {}", + file_set_in.name() + )) + .color(Color::Muted), + ) + }, + ), ) .child( Label::new(SharedString::new_static(setting_item.description)) @@ -372,6 +405,7 @@ impl SettingsPageItem { ) .child(renderer.render( setting_item.field.as_ref(), + file, setting_item.metadata.as_deref(), window, cx, @@ -391,36 +425,53 @@ struct SettingItem { #[allow(unused)] #[derive(Clone, PartialEq)] -enum SettingsFile { +enum SettingsUiFile { User, // Uses all settings. Local((WorktreeId, Arc)), // Has a special name, and special set of settings Server(&'static str), // Uses a special name, and the user settings } -impl SettingsFile { +impl SettingsUiFile { fn pages(&self) -> Vec { match self { - SettingsFile::User => user_settings_data(), - SettingsFile::Local(_) => project_settings_data(), - SettingsFile::Server(_) => user_settings_data(), + SettingsUiFile::User => user_settings_data(), + SettingsUiFile::Local(_) => project_settings_data(), + SettingsUiFile::Server(_) => user_settings_data(), } } fn name(&self) -> SharedString { match self { - SettingsFile::User => SharedString::new_static("User"), + SettingsUiFile::User => SharedString::new_static("User"), // TODO is PathStyle::local() ever not appropriate? - SettingsFile::Local((_, path)) => { + SettingsUiFile::Local((_, path)) => { format!("Local ({})", path.display(PathStyle::local())).into() } - SettingsFile::Server(file) => format!("Server ({})", file).into(), + SettingsUiFile::Server(file) => format!("Server ({})", file).into(), + } + } + + fn from_settings(file: settings::SettingsFile) -> Option { + Some(match file { + settings::SettingsFile::User => SettingsUiFile::User, + settings::SettingsFile::Local(location) => SettingsUiFile::Local(location), + settings::SettingsFile::Server => SettingsUiFile::Server("todo: server name"), + settings::SettingsFile::Default => return None, + }) + } + + fn to_settings(&self) -> settings::SettingsFile { + match self { + SettingsUiFile::User => settings::SettingsFile::User, + SettingsUiFile::Local(location) => settings::SettingsFile::Local(location.clone()), + SettingsUiFile::Server(_) => settings::SettingsFile::Server, } } } impl SettingsWindow { pub fn new(window: &mut Window, cx: &mut Context) -> Self { - let current_file = SettingsFile::User; + let current_file = SettingsUiFile::User; let search = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_placeholder_text("Search settings…", window, cx); @@ -496,26 +547,21 @@ impl SettingsWindow { let mut ui_files = vec![]; let all_files = settings_store.get_all_files(); for file in all_files { - let settings_ui_file = match file { - settings::SettingsFile::User => SettingsFile::User, - settings::SettingsFile::Global => continue, - settings::SettingsFile::Extension => continue, - settings::SettingsFile::Server => SettingsFile::Server("todo: server name"), - settings::SettingsFile::Default => continue, - settings::SettingsFile::Local(location) => SettingsFile::Local(location), + let Some(settings_ui_file) = SettingsUiFile::from_settings(file) else { + continue; }; ui_files.push(settings_ui_file); } ui_files.reverse(); - if !ui_files.contains(&self.current_file) { + self.files = ui_files; + if !self.files.contains(&self.current_file) { self.change_file(0, cx); } - self.files = ui_files; } fn change_file(&mut self, ix: usize, cx: &mut Context) { if ix >= self.files.len() { - self.current_file = SettingsFile::User; + self.current_file = SettingsUiFile::User; return; } if self.files[ix] == self.current_file { @@ -682,25 +728,19 @@ impl Render for SettingsWindow { } } +// fn read_field(pick: fn(&SettingsContent) -> &Option, file: SettingsFile, cx: &App) -> Option { +// let (_, value) = cx.global::().get_value_from_file(file.to_settings(), (), pick); +// } + fn render_text_field( - field: SettingField>, + field: SettingField, + file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, cx: &mut App, ) -> AnyElement { - // TODO: in settings window state - let store = SettingsStore::global(cx); - - // TODO: This clone needs to go!! - let defaults = store.raw_default_settings().clone(); - let user_settings = store - .raw_user_settings() - .cloned() - .unwrap_or_default() - .content; - - let initial_text = (field.pick)(&user_settings) - .clone() - .or_else(|| (field.pick)(&defaults).clone()); + let (_, initial_text) = + SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); + let initial_text = Some(initial_text.clone()).filter(|s| !s.is_empty()); SettingsEditor::new() .when_some(initial_text, |editor, text| editor.with_initial_text(text)) @@ -719,24 +759,13 @@ fn render_text_field( } fn render_toggle_button + From + Copy>( - field: SettingField>, + field: SettingField, + file: SettingsUiFile, cx: &mut App, ) -> AnyElement { - // TODO: in settings window state - let store = SettingsStore::global(cx); - - // TODO: This clone needs to go!! - let defaults = store.raw_default_settings().clone(); - let user_settings = store - .raw_user_settings() - .cloned() - .unwrap_or_default() - .content; - - let toggle_state = if (field.pick)(&user_settings) - .unwrap_or_else(|| (field.pick)(&defaults).unwrap()) - .into() - { + let (_, &value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); + + let toggle_state = if value.into() { ui::ToggleState::Selected } else { ui::ToggleState::Unselected @@ -746,7 +775,7 @@ fn render_toggle_button + From + Copy>( .on_click({ move |state, _window, cx| { let state = *state == ui::ToggleState::Selected; - let field = field.clone(); + let field = field; cx.update_global(move |store: &mut SettingsStore, cx| { store.update_settings_file(::global(cx), move |settings, _cx| { *(field.pick_mut)(settings) = Some(state.into()); @@ -758,7 +787,8 @@ fn render_toggle_button + From + Copy>( } fn render_dropdown( - field: SettingField>, + field: SettingField, + file: SettingsUiFile, window: &mut Window, cx: &mut App, ) -> AnyElement @@ -768,16 +798,9 @@ where let variants = || -> &'static [T] { ::VARIANTS }; let labels = || -> &'static [&'static str] { ::VARIANTS }; - let store = SettingsStore::global(cx); - let defaults = store.raw_default_settings().clone(); - let user_settings = store - .raw_user_settings() - .cloned() - .unwrap_or_default() - .content; + let (_, ¤t_value) = + SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); - let current_value = - (field.pick)(&user_settings).unwrap_or_else(|| (field.pick)(&defaults).unwrap()); let current_value_label = labels()[variants().iter().position(|v| *v == current_value).unwrap()]; @@ -894,7 +917,7 @@ mod test { let mut settings_window = SettingsWindow { files: Vec::default(), - current_file: crate::SettingsFile::User, + current_file: crate::SettingsUiFile::User, pages, search: cx.new(|cx| Editor::single_line(window, cx)), navbar_entry: selected_idx.unwrap(), diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 582a40021dc7c552d784134f2abeab44f1bf44bc..3e8fc6114a7ac0ca10fbe823fff9ab3a7115b3c4 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -51,7 +51,7 @@ impl Settings for WorktreeSettings { .collect(); Self { - project_name: None, + project_name: worktree.project_name.filter(|p| !p.is_empty()), file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions") .log_err() .unwrap_or_default(),