diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index eb84bc68cc648c97f25ad1619d9ce83e6e193a6c..be725660e8bffdb50715b784bef8766f7e5e88eb 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -220,7 +220,18 @@ impl UserSettingsContent { /// /// Default: VSCode #[derive( - Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, Default, + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + Default, + strum::VariantArray, + strum::VariantNames, )] pub enum BaseKeymapContent { #[default] @@ -313,7 +324,7 @@ pub struct AudioSettingsContent { /// Control what info is collected by Zed. #[skip_serializing_none] -#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug, MergeFrom)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug, MergeFrom)] pub struct TelemetrySettingsContent { /// Send debug info like crash reports. /// @@ -325,6 +336,15 @@ pub struct TelemetrySettingsContent { pub metrics: Option, } +impl Default for TelemetrySettingsContent { + fn default() -> Self { + Self { + diagnostics: Some(true), + metrics: Some(true), + } + } +} + #[skip_serializing_none] #[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Clone, MergeFrom)] pub struct DebuggerSettingsContent { @@ -706,7 +726,19 @@ pub struct OutlinePanelSettingsContent { pub expand_outlines_with_depth: Option, } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Copy, PartialEq)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum DockSide { Left, diff --git a/crates/settings/src/settings_content/editor.rs b/crates/settings/src/settings_content/editor.rs index d4879403a906399d81c0b0079167f8aa9673fd76..edfbc106538aed42d804f13954271f348ab16485 100644 --- a/crates/settings/src/settings_content/editor.rs +++ b/crates/settings/src/settings_content/editor.rs @@ -363,7 +363,19 @@ pub enum DocumentColorsRenderMode { Background, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum CurrentLineHighlight { // Don't highlight the current line. @@ -377,7 +389,19 @@ pub enum CurrentLineHighlight { } /// When to populate a new search's query based on the text under the cursor. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum SeedQuerySetting { /// Always populate the search query with the word under the cursor. @@ -390,7 +414,18 @@ pub enum SeedQuerySetting { /// What to do when multibuffer is double clicked in some of its excerpts (parts of singleton buffers). #[derive( - Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, + Default, + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum DoubleClickInMultibuffer { @@ -459,7 +494,19 @@ pub enum ScrollbarDiagnostics { /// The key to use for adding multiple cursors /// /// Default: alt -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum MultiCursorModifier { Alt, @@ -470,7 +517,19 @@ pub enum MultiCursorModifier { /// Whether the editor will scroll beyond the last line. /// /// Default: one_page -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum ScrollBeyondLastLine { /// The editor will not scroll beyond the last line. @@ -513,7 +572,18 @@ pub enum CursorShape { /// What to do when go to definition yields no results. #[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, + Copy, + Clone, + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum GoToDefinitionFallback { @@ -528,7 +598,18 @@ pub enum GoToDefinitionFallback { /// /// Default: on_typing_and_movement #[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, + Copy, + Clone, + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum HideMouseMode { @@ -545,7 +626,18 @@ pub enum HideMouseMode { /// /// Default: inline #[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, + Copy, + Clone, + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum SnippetSortOrder { diff --git a/crates/settings/src/settings_content/language.rs b/crates/settings/src/settings_content/language.rs index fe04e9fff638252d4586a32fd6031ca0d22b6771..d60fbe04eb9cdec9ddc36c7fd827bcfea499293a 100644 --- a/crates/settings/src/settings_content/language.rs +++ b/crates/settings/src/settings_content/language.rs @@ -147,7 +147,19 @@ pub enum EditPredictionsMode { } /// Controls the soft-wrapping behavior in the editor. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum SoftWrap { /// Prefer a single line generally, unless an overly long line is encountered. @@ -333,7 +345,19 @@ pub struct LanguageSettingsContent { } /// Controls how whitespace should be displayedin the editor. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum ShowWhitespaceSetting { /// Draw whitespace only for the selected text. diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs index dce2a39d32d7b570407c209668bebf56d6c34704..81398e593df92ad6d06dba411a108dc5f4b7c65c 100644 --- a/crates/settings/src/settings_content/project.rs +++ b/crates/settings/src/settings_content/project.rs @@ -268,7 +268,19 @@ pub struct GitSettings { pub hunk_style: Option, } -#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum GitGutterSetting { /// Show git gutter in tracked files. @@ -318,7 +330,7 @@ pub struct BlameSettings { } #[skip_serializing_none] -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] #[serde(rename_all = "snake_case")] pub struct BranchPickerSettingsContent { /// Whether to show author name as part of the commit information. @@ -327,7 +339,19 @@ pub struct BranchPickerSettingsContent { pub show_author_name: Option, } -#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive( + Clone, + Copy, + PartialEq, + Debug, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum GitHunkStyleSetting { /// Show unstaged hunks with a filled background and staged hunks hollow. @@ -338,7 +362,7 @@ pub enum GitHunkStyleSetting { } #[skip_serializing_none] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] pub struct DiagnosticsSettingsContent { /// Whether to show the project diagnostics button in the status bar. pub button: Option, @@ -354,7 +378,9 @@ pub struct DiagnosticsSettingsContent { } #[skip_serializing_none] -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive( + Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, +)] pub struct LspPullDiagnosticsSettingsContent { /// Whether to pull for diagnostics or not. /// @@ -368,7 +394,9 @@ pub struct LspPullDiagnosticsSettingsContent { } #[skip_serializing_none] -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Eq)] +#[derive( + Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Eq, +)] pub struct InlineDiagnosticsSettingsContent { /// Whether or not to show inline diagnostics /// @@ -427,6 +455,8 @@ pub enum DirenvSettings { Deserialize, JsonSchema, MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum DiagnosticSeverityContent { diff --git a/crates/settings/src/settings_content/terminal.rs b/crates/settings/src/settings_content/terminal.rs index 29294c1e55a8994f2a5c1fbbe060789fb38545dd..9b7813c55b3b1ba9be56c13dfdaf7dbec13cd20a 100644 --- a/crates/settings/src/settings_content/terminal.rs +++ b/crates/settings/src/settings_content/terminal.rs @@ -301,7 +301,19 @@ impl VenvSettings { } } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum TerminalDockPosition { Left, diff --git a/crates/settings/src/settings_content/theme.rs b/crates/settings/src/settings_content/theme.rs index bc6eed96ad12ef5f1358e7a21a4fe2db8a6a9d10..84dd9ce88820bd4820037d41eee0f9ded610da39 100644 --- a/crates/settings/src/settings_content/theme.rs +++ b/crates/settings/src/settings_content/theme.rs @@ -200,8 +200,37 @@ impl UiDensity { #[serde(transparent)] pub struct FontFamilyName(pub Arc); +impl AsRef for FontFamilyName { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl From for FontFamilyName { + fn from(value: String) -> Self { + Self(Arc::from(value)) + } +} + +impl From for String { + fn from(value: FontFamilyName) -> Self { + value.0.to_string() + } +} + /// The buffer's line height. -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)] +#[derive( + Clone, + Copy, + Debug, + Serialize, + Deserialize, + PartialEq, + JsonSchema, + MergeFrom, + Default, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum BufferLineHeight { /// A less dense line height. @@ -213,6 +242,10 @@ pub enum BufferLineHeight { Custom(#[serde(deserialize_with = "deserialize_line_height")] f32), } +impl strum::VariantArray for BufferLineHeight { + const VARIANTS: &'static [Self] = &[Self::Comfortable, Self::Standard]; +} + fn deserialize_line_height<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/crates/settings/src/settings_content/workspace.rs b/crates/settings/src/settings_content/workspace.rs index 9d210a73da1a92768e4e5cbb5e9804ce9d292ce5..66b46c4c719ba82aa237cc9034e9c57bb1c4449d 100644 --- a/crates/settings/src/settings_content/workspace.rs +++ b/crates/settings/src/settings_content/workspace.rs @@ -156,7 +156,19 @@ pub struct PreviewTabsSettingsContent { pub enable_preview_from_code_navigation: Option, } -#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "lowercase")] pub enum ClosePosition { Left, @@ -164,7 +176,19 @@ pub enum ClosePosition { Right, } -#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "lowercase")] pub enum ShowCloseButton { Always, @@ -174,7 +198,18 @@ pub enum ShowCloseButton { } #[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, + Copy, + Clone, + Debug, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum ShowDiagnostics { @@ -184,7 +219,19 @@ pub enum ShowDiagnostics { All, } -#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum ActivateOnClose { #[default] @@ -212,7 +259,19 @@ pub struct ActivePanelModifiers { pub inactive_opacity: Option, } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)] +#[derive( + Copy, + Clone, + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum BottomDockLayout { /// Contained between the left and right docks @@ -226,7 +285,19 @@ pub enum BottomDockLayout { RightAligned, } -#[derive(Copy, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)] +#[derive( + Copy, + Clone, + PartialEq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + Debug, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum CloseWindowWhenNoItems { /// Match platform conventions by default, so "on" on macOS and "off" everywhere else @@ -249,7 +320,18 @@ impl CloseWindowWhenNoItems { } #[derive( - Copy, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, + Copy, + Clone, + PartialEq, + Eq, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + Debug, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum RestoreOnStartupBehavior { @@ -333,7 +415,19 @@ pub struct CenteredLayoutSettings { pub right_padding: Option, } -#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Debug)] +#[derive( + Copy, + Clone, + Default, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Debug, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum OnLastWindowClosed { /// Match platform conventions by default, so don't quit on macOS, and quit on other platforms diff --git a/crates/settings_ui/src/components.rs b/crates/settings_ui/src/components.rs index 35afed363123d42a024d417aebb32da7ba39a316..f2fc7e74dbd13d1af3a0d89bd522500e00933c07 100644 --- a/crates/settings_ui/src/components.rs +++ b/crates/settings_ui/src/components.rs @@ -49,6 +49,8 @@ impl RenderOnce for SettingsEditor { if let Some(placeholder) = self.placeholder { editor.set_placeholder_text(placeholder, window, cx); } + // todo(settings_ui): We should have an observe global use for settings store + // so whenever a settings file is updated, the settings ui updates too editor } }); diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index b05335dd6f541ead35f4e703f63de2eb067dc5e9..d1edc7f424e3d590551a8566d5fcc17643538272 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -4,11 +4,15 @@ use editor::{Editor, EditorEvent}; use feature_flags::{FeatureFlag, FeatureFlagAppExt as _}; use fuzzy::StringMatchCandidate; use gpui::{ - App, Div, Entity, Global, ReadGlobal as _, Task, TitlebarOptions, UniformListScrollHandle, - Window, WindowHandle, WindowOptions, actions, div, point, px, size, uniform_list, + App, AppContext as _, Context, Div, Entity, Global, IntoElement, ReadGlobal as _, Render, + ScrollHandle, Stateful, Task, TitlebarOptions, UniformListScrollHandle, Window, WindowHandle, + WindowOptions, actions, div, point, px, size, uniform_list, }; use project::WorktreeId; -use settings::{CursorShape, SaturatingBool, SettingsContent, SettingsStore}; +use settings::{ + BottomDockLayout, CloseWindowWhenNoItems, CursorShape, OnLastWindowClosed, + RestoreOnStartupBehavior, SaturatingBool, SettingsContent, SettingsStore, +}; use std::{ any::{Any, TypeId, type_name}, cell::RefCell, @@ -138,34 +142,2057 @@ struct SettingsFieldMetadata { fn user_settings_data() -> Vec { vec![ SettingsPage { - title: "General Page", - expanded: true, + title: "General Page", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("General"), + SettingsPageItem::SettingItem(SettingItem { + title: "Confirm Quit", + description: "Whether to confirm before quitting Zed", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.confirm_quit, + pick_mut: |settings_content| &mut settings_content.workspace.confirm_quit, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Restore On Startup", + description: "Whether to restore previous session when opening Zed", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.restore_on_startup, + pick_mut: |settings_content| { + &mut settings_content.workspace.restore_on_startup + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Restore File State", + description: "Whether to restore previous file state when reopening", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.restore_on_file_reopen, + pick_mut: |settings_content| { + &mut settings_content.workspace.restore_on_file_reopen + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Close on File Delete", + description: "Whether to automatically close files that have been deleted", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.close_on_file_delete, + pick_mut: |settings_content| { + &mut settings_content.workspace.close_on_file_delete + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "When Closing With No Tabs", + description: "What to do when using 'close active item' with no tabs", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.workspace.when_closing_with_no_tabs + }, + pick_mut: |settings_content| { + &mut settings_content.workspace.when_closing_with_no_tabs + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "On Last Window Closed", + description: "What to do when the last window is closed", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.on_last_window_closed, + pick_mut: |settings_content| { + &mut settings_content.workspace.on_last_window_closed + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use System Path Prompts", + description: "Whether to use system dialogs for Open and Save As", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.workspace.use_system_path_prompts + }, + pick_mut: |settings_content| { + &mut settings_content.workspace.use_system_path_prompts + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use System Prompts", + description: "Whether to use system prompts for confirmations", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.use_system_prompts, + pick_mut: |settings_content| { + &mut settings_content.workspace.use_system_prompts + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Scoped Settings"), + // todo(settings_ui): Implement another setting item type that just shows an edit in settings.json + // SettingsPageItem::SettingItem(SettingItem { + // title: "Preview Channel", + // description: "Which settings should be activated only in Preview build of Zed", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.workspace.use_system_prompts, + // pick_mut: |settings_content| { + // &mut settings_content.workspace.use_system_prompts + // }, + // }), + // metadata: None, + // }), + // SettingsPageItem::SettingItem(SettingItem { + // title: "Settings Profiles", + // description: "Any number of settings profiles that are temporarily applied on top of your existing user settings.", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.workspace.use_system_prompts, + // pick_mut: |settings_content| { + // &mut settings_content.workspace.use_system_prompts + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Privacy"), + SettingsPageItem::SettingItem(SettingItem { + title: "Telemetry Diagnostics", + description: "Send debug info like crash reports.", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(telemetry) = &settings_content.telemetry { + &telemetry.diagnostics + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .telemetry + .get_or_insert_default() + .diagnostics + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Telemetry Metrics", + description: "Send anonymized usage data like what languages you're using Zed with.", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(telemetry) = &settings_content.telemetry { + &telemetry.metrics + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.telemetry.get_or_insert_default().metrics + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "Appearance & Behavior", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Theme"), + // todo(settings_ui): Figure out how we want to add these + // SettingsPageItem::SettingItem(SettingItem { + // title: "Theme Mode", + // description: "How to select the theme", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.theme, + // pick_mut: |settings_content| &mut settings_content.theme.theme, + // }), + // metadata: None, + // }), + // SettingsPageItem::SettingItem(SettingItem { + // title: "Icon Theme", + // // todo(settings_ui) + // // This description is misleading because the icon theme is used in more places than the file explorer) + // description: "Choose the icon theme for file explorer", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.icon_theme, + // pick_mut: |settings_content| &mut settings_content.theme.icon_theme, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Layout"), + SettingsPageItem::SettingItem(SettingItem { + title: "Bottom Dock Layout", + description: "Layout mode for the bottom dock", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.bottom_dock_layout, + pick_mut: |settings_content| { + &mut settings_content.workspace.bottom_dock_layout + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Zoomed Padding", + description: "Whether to show padding for zoomed panels", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.zoomed_padding, + pick_mut: |settings_content| &mut settings_content.workspace.zoomed_padding, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use System Window Tabs", + description: "Whether to allow windows to tab together based on the user's tabbing preference (macOS only)", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.use_system_window_tabs, + pick_mut: |settings_content| { + &mut settings_content.workspace.use_system_window_tabs + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Fonts"), + SettingsPageItem::SettingItem(SettingItem { + title: "Buffer Font Family", + description: "Font family for editor text", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.theme.buffer_font_family, + pick_mut: |settings_content| &mut settings_content.theme.buffer_font_family, + }), + metadata: None, + }), + // todo(settings_ui): We need to implement a numeric stepper for these + // SettingsPageItem::SettingItem(SettingItem { + // title: "Buffer Font Size", + // description: "Font size for editor text", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.buffer_font_size, + // pick_mut: |settings_content| &mut settings_content.theme.buffer_font_size, + // }), + // metadata: None, + // }), + // SettingsPageItem::SettingItem(SettingItem { + // title: "Buffer Font Weight", + // description: "Font weight for editor text (100-900)", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.buffer_font_weight, + // pick_mut: |settings_content| &mut settings_content.theme.buffer_font_weight, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Buffer Line Height", + description: "Line height for editor text", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.theme.buffer_line_height, + pick_mut: |settings_content| &mut settings_content.theme.buffer_line_height, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "UI Font Family", + description: "Font family for UI elements", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.theme.ui_font_family, + pick_mut: |settings_content| &mut settings_content.theme.ui_font_family, + }), + metadata: None, + }), + // todo(settings_ui): We need to implement a numeric stepper for these + // SettingsPageItem::SettingItem(SettingItem { + // title: "UI Font Size", + // description: "Font size for UI elements", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.ui_font_size, + // pick_mut: |settings_content| &mut settings_content.theme.ui_font_size, + // }), + // metadata: None, + // }), + // SettingsPageItem::SettingItem(SettingItem { + // title: "UI Font Weight", + // description: "Font weight for UI elements (100-900)", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.ui_font_weight, + // pick_mut: |settings_content| &mut settings_content.theme.ui_font_weight, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Keymap"), + SettingsPageItem::SettingItem(SettingItem { + title: "Base Keymap", + description: "The name of a base set of key bindings to use", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.base_keymap, + pick_mut: |settings_content| &mut settings_content.base_keymap, + }), + metadata: None, + }), + // todo(settings_ui): Vim/Helix Mode should be apart of one type because it's undefined + // behavior to have them both enabled at the same time + SettingsPageItem::SettingItem(SettingItem { + title: "Vim Mode", + description: "Whether to enable vim modes and key bindings", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.vim_mode, + pick_mut: |settings_content| &mut settings_content.vim_mode, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Helix Mode", + description: "Whether to enable helix modes and key bindings", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.helix_mode, + pick_mut: |settings_content| &mut settings_content.helix_mode, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Multi Cursor Modifier", + description: "Modifier key for adding multiple cursors", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.multi_cursor_modifier, + pick_mut: |settings_content| { + &mut settings_content.editor.multi_cursor_modifier + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Cursor"), + SettingsPageItem::SettingItem(SettingItem { + title: "Cursor Blink", + description: "Whether the cursor blinks in the editor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.cursor_blink, + pick_mut: |settings_content| &mut settings_content.editor.cursor_blink, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Cursor Shape", + description: "Cursor shape for the editor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.cursor_shape, + pick_mut: |settings_content| &mut settings_content.editor.cursor_shape, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Hide Mouse", + description: "When to hide the mouse cursor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.hide_mouse, + pick_mut: |settings_content| &mut settings_content.editor.hide_mouse, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Highlighting"), + // todo(settings_ui): numeric stepper and validator is needed for this + // SettingsPageItem::SettingItem(SettingItem { + // title: "Unnecessary Code Fade", + // description: "How much to fade out unused code (0.0 - 0.9)", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.theme.unnecessary_code_fade, + // pick_mut: |settings_content| &mut settings_content.theme.unnecessary_code_fade, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Current Line Highlight", + description: "How to highlight the current line", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.current_line_highlight, + pick_mut: |settings_content| { + &mut settings_content.editor.current_line_highlight + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Selection Highlight", + description: "Whether to highlight all occurrences of selected text", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.selection_highlight, + pick_mut: |settings_content| { + &mut settings_content.editor.selection_highlight + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Rounded Selection", + description: "Whether the text selection should have rounded corners", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.rounded_selection, + pick_mut: |settings_content| &mut settings_content.editor.rounded_selection, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Guides"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Wrap Guides", + description: "Whether to show wrap guides (vertical rulers)", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_wrap_guides + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_wrap_guides + }, + }), + metadata: None, + }), + // todo(settings_ui): This needs a custom component + // SettingsPageItem::SettingItem(SettingItem { + // title: "Wrap Guides", + // description: "Character counts at which to show wrap guides", + // field: Box::new(SettingField { + // pick: |settings_content| { + // &settings_content + // .project + // .all_languages + // .defaults + // .wrap_guides + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .project + // .all_languages + // .defaults + // .wrap_guides + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Whitespace"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Whitespace", + description: "Whether to show tabs and spaces", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_whitespaces + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_whitespaces + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Window"), + // todo(settings_ui): Should we filter by platform? + SettingsPageItem::SettingItem(SettingItem { + title: "Use System Window Tabs", + description: "Whether to allow windows to tab together (macOS only)", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.use_system_window_tabs, + pick_mut: |settings_content| { + &mut settings_content.workspace.use_system_window_tabs + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Layout"), + SettingsPageItem::SettingItem(SettingItem { + title: "Zoomed Padding", + description: "Whether to show padding for zoomed panels", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.zoomed_padding, + pick_mut: |settings_content| &mut settings_content.workspace.zoomed_padding, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Centered Layout Left Padding", + // description: "Left padding for cenetered layout", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.workspace.bottom_dock_layout, + // pick_mut: |settings_content| { + // &mut settings_content.workspace.bottom_dock_layout + // }, + // }), + // metadata: None, + // }), + // SettingsPageItem::SettingItem(SettingItem { + // title: "Centered Layout Right Padding", + // description: "Right padding for cenetered layout", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.workspace.bottom_dock_layout, + // pick_mut: |settings_content| { + // &mut settings_content.workspace.bottom_dock_layout + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Bottom Dock Layout", + description: "Layout mode of the bottom dock", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.bottom_dock_layout, + pick_mut: |settings_content| { + &mut settings_content.workspace.bottom_dock_layout + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "Editor", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Indentation"), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Tab Size", + // description: "How many columns a tab should occupy", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.project.all_languages.defaults.tab_size, + // pick_mut: |settings_content| &mut settings_content.project.all_languages.defaults.tab_size, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Hard Tabs", + description: "Whether to indent lines using tab characters, as opposed to multiple spaces", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.defaults.hard_tabs + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.hard_tabs + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Indent", + description: "Whether indentation should be adjusted based on the context whilst typing", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.defaults.auto_indent + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.auto_indent + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Indent On Paste", + description: "Whether indentation of pasted content should be adjusted based on the context", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .auto_indent_on_paste + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .auto_indent_on_paste + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Wrapping"), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Preferred Line Length", + // description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.project.all_languages.defaults.preferred_line_length, + // pick_mut: |settings_content| &mut settings_content.project.all_languages.defaults.preferred_line_length, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Soft Wrap", + description: "How to soft-wrap long lines of text", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.defaults.soft_wrap + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.soft_wrap + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Search"), + SettingsPageItem::SettingItem(SettingItem { + title: "Search Wrap", + description: "Whether the editor search results will loop", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.search_wrap, + pick_mut: |settings_content| &mut settings_content.editor.search_wrap, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Seed Search Query From Cursor", + description: "When to populate a new search's query based on the text under the cursor", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.editor.seed_search_query_from_cursor + }, + pick_mut: |settings_content| { + &mut settings_content.editor.seed_search_query_from_cursor + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Smartcase Search", + description: "Whether to use smartcase search", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.use_smartcase_search, + pick_mut: |settings_content| { + &mut settings_content.editor.use_smartcase_search + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Editor Behavior"), + SettingsPageItem::SettingItem(SettingItem { + title: "Redact Private Values", + description: "Hide the values of variables in private files", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.redact_private_values, + pick_mut: |settings_content| { + &mut settings_content.editor.redact_private_values + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Middle Click Paste", + description: "Whether to enable middle-click paste on Linux", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.middle_click_paste, + pick_mut: |settings_content| { + &mut settings_content.editor.middle_click_paste + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Double Click In Multibuffer", + description: "What to do when multibuffer is double clicked in some of its excerpts", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.editor.double_click_in_multibuffer + }, + pick_mut: |settings_content| { + &mut settings_content.editor.double_click_in_multibuffer + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Go To Definition Fallback", + description: "Whether to follow-up empty go to definition responses from the language server", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.go_to_definition_fallback, + pick_mut: |settings_content| { + &mut settings_content.editor.go_to_definition_fallback + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Scrolling"), + SettingsPageItem::SettingItem(SettingItem { + title: "Scroll Beyond Last Line", + description: "Whether the editor will scroll beyond the last line", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.scroll_beyond_last_line, + pick_mut: |settings_content| { + &mut settings_content.editor.scroll_beyond_last_line + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Vertical Scroll Margin", + // description: "The number of lines to keep above/below the cursor when auto-scrolling", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.editor.vertical_scroll_margin, + // pick_mut: |settings_content| &mut settings_content.editor.vertical_scroll_margin, + // }), + // metadata: None, + // }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Horizontal Scroll Margin", + // description: "The number of characters to keep on either side when scrolling with the mouse", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.editor.horizontal_scroll_margin, + // pick_mut: |settings_content| &mut settings_content.editor.horizontal_scroll_margin, + // }), + // metadata: None, + // }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Scroll Sensitivity", + // description: "Scroll sensitivity multiplier", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.editor.scroll_sensitivity, + // pick_mut: |settings_content| &mut settings_content.editor.scroll_sensitivity, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Autoscroll On Clicks", + description: "Whether to scroll when clicking near the edge of the visible text area", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.autoscroll_on_clicks, + pick_mut: |settings_content| { + &mut settings_content.editor.autoscroll_on_clicks + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Auto Actions"), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Autoclose", + description: "Whether to automatically type closing characters for you", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_autoclose + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_autoclose + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Auto Surround", + description: "Whether to automatically surround text with characters for you", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_auto_surround + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_auto_surround + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use On Type Format", + description: "Whether to use additional LSP queries to format the code after every trigger symbol input", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_on_type_format + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_on_type_format + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Always Treat Brackets As Autoclosed", + description: "Controls how the editor handles the autoclosed characters", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .always_treat_brackets_as_autoclosed + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .always_treat_brackets_as_autoclosed + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Formatting"), + SettingsPageItem::SettingItem(SettingItem { + title: "Remove Trailing Whitespace On Save", + description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .remove_trailing_whitespace_on_save + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .remove_trailing_whitespace_on_save + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Ensure Final Newline On Save", + description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .ensure_final_newline_on_save + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .ensure_final_newline_on_save + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Extend Comment On Newline", + description: "Whether to start a new line with a comment when a previous line is a comment as well", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .extend_comment_on_newline + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .extend_comment_on_newline + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Completions"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Completions On Input", + description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_completions_on_input + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_completions_on_input + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Completion Documentation", + description: "Whether to display inline and alongside documentation for items in the completions menu", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_completion_documentation + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_completion_documentation + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Signature Help", + description: "Whether to automatically show a signature help pop-up or not", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.auto_signature_help, + pick_mut: |settings_content| { + &mut settings_content.editor.auto_signature_help + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Signature Help After Edits", + description: "Whether to show the signature help pop-up after completions or bracket pairs inserted", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.editor.show_signature_help_after_edits + }, + pick_mut: |settings_content| { + &mut settings_content.editor.show_signature_help_after_edits + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Snippet Sort Order", + description: "Determines how snippets are sorted relative to other completion items", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.snippet_sort_order, + pick_mut: |settings_content| { + &mut settings_content.editor.snippet_sort_order + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Hover"), + SettingsPageItem::SettingItem(SettingItem { + title: "Hover Popover Enabled", + description: "Whether to show the informational hover box when moving the mouse over symbols in the editor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.hover_popover_enabled, + pick_mut: |settings_content| { + &mut settings_content.editor.hover_popover_enabled + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Hover Popover Delay", + // description: "Time to wait in milliseconds before showing the informational hover box", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.editor.hover_popover_delay, + // pick_mut: |settings_content| &mut settings_content.editor.hover_popover_delay, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Code Actions"), + SettingsPageItem::SettingItem(SettingItem { + title: "Inline Code Actions", + description: "Whether to show code action button at start of buffer line", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.inline_code_actions, + pick_mut: |settings_content| { + &mut settings_content.editor.inline_code_actions + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Selection"), + SettingsPageItem::SettingItem(SettingItem { + title: "Drag And Drop Selection", + description: "Whether to enable drag and drop selection", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(drag_and_drop) = + &settings_content.editor.drag_and_drop_selection + { + &drag_and_drop.enabled + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .drag_and_drop_selection + .get_or_insert_default() + .enabled + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Drag And Drop Selection Delay", + // description: "Delay in milliseconds before drag and drop selection starts", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(drag_and_drop) = &settings_content.editor.drag_and_drop_selection { + // &drag_and_drop.delay + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content.editor.drag_and_drop_selection.get_or_insert_default().delay + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Line Numbers"), + SettingsPageItem::SettingItem(SettingItem { + title: "Relative Line Numbers", + description: "Whether the line numbers on editors gutter are relative or not", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.relative_line_numbers, + pick_mut: |settings_content| { + &mut settings_content.editor.relative_line_numbers + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Gutter"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Line Numbers", + description: "Whether to show line numbers in the gutter", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(gutter) = &settings_content.editor.gutter { + &gutter.line_numbers + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .gutter + .get_or_insert_default() + .line_numbers + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Runnables", + description: "Whether to show runnable buttons in the gutter", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(gutter) = &settings_content.editor.gutter { + &gutter.runnables + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .gutter + .get_or_insert_default() + .runnables + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Breakpoints", + description: "Whether to show breakpoints in the gutter", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(gutter) = &settings_content.editor.gutter { + &gutter.breakpoints + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .gutter + .get_or_insert_default() + .breakpoints + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Folds", + description: "Whether to show code folding controls in the gutter", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(gutter) = &settings_content.editor.gutter { + &gutter.folds + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.editor.gutter.get_or_insert_default().folds + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Tabs"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Tab Bar", + description: "Whether or not to show the tab bar in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tab_bar) = &settings_content.tab_bar { + &tab_bar.show + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.tab_bar.get_or_insert_default().show + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Git Status In Tabs", + description: "Whether to show the Git file status on a tab item", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.git_status + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.tabs.get_or_insert_default().git_status + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show File Icons In Tabs", + description: "Whether to show the file icon for a tab", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.file_icons + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.tabs.get_or_insert_default().file_icons + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Tab Close Position", + description: "Position of the close button in a tab", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.close_position + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.tabs.get_or_insert_default().close_position + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Maximum Tabs", + // description: "Maximum open tabs in a pane. Will not close an unsaved tab", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.workspace.max_tabs, + // pick_mut: |settings_content| &mut settings_content.workspace.max_tabs, + // }), + // metadata: None, + // }), + ], + }, + SettingsPage { + title: "Workbench & Window", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Workbench"), + SettingsPageItem::SettingItem(SettingItem { + title: "Editor Tabs", + description: "Whether or not to show the tab bar in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tab_bar) = &settings_content.tab_bar { + &tab_bar.show + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.tab_bar.get_or_insert_default().show + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Active language Button", + description: "Whether to show the active language button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(status_bar) = &settings_content.editor.status_bar { + &status_bar.active_language_button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .status_bar + .get_or_insert_default() + .active_language_button + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Cursor Position Button", + description: "Whether to show the cursor position button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(status_bar) = &settings_content.editor.status_bar { + &status_bar.cursor_position_button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .status_bar + .get_or_insert_default() + .cursor_position_button + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Terminal"), + SettingsPageItem::SettingItem(SettingItem { + title: "Terminal Button", + description: "Whether to show the terminal button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(terminal) = &settings_content.terminal { + &terminal.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.terminal.get_or_insert_default().button + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Navigation History Buttons", + description: "Whether or not to show the navigation history buttons in the tab bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tab_bar) = &settings_content.tab_bar { + &tab_bar.show_nav_history_buttons + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .tab_bar + .get_or_insert_default() + .show_nav_history_buttons + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "Panels & Tools", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Project Panel"), + SettingsPageItem::SettingItem(SettingItem { + title: "Project Panel Button", + description: "Whether to show the project panel button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(project_panel) = &settings_content.project_panel { + &project_panel.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .project_panel + .get_or_insert_default() + .button + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Project Panel Dock", + description: "Where to dock the project panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(project_panel) = &settings_content.project_panel { + &project_panel.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.project_panel.get_or_insert_default().dock + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Project Panel Default Width", + // description: "Default width of the project panel in pixels", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(project_panel) = &settings_content.project_panel { + // &project_panel.default_width + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .project_panel + // .get_or_insert_default() + // .default_width + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Terminal"), + SettingsPageItem::SettingItem(SettingItem { + title: "Terminal Dock", + description: "Where to dock the terminal panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(terminal) = &settings_content.terminal { + &terminal.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.terminal.get_or_insert_default().dock + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Tab Settings"), + SettingsPageItem::SettingItem(SettingItem { + title: "Activate On Close", + description: "What to do after closing the current tab", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.activate_on_close + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .tabs + .get_or_insert_default() + .activate_on_close + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Tab Show Diagnostics", + description: "Which files containing diagnostic errors/warnings to mark in the tabs", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.show_diagnostics + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .tabs + .get_or_insert_default() + .show_diagnostics + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Close Button", + description: "Controls the appearance behavior of the tab's close button", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(tabs) = &settings_content.tabs { + &tabs.show_close_button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .tabs + .get_or_insert_default() + .show_close_button + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Preview Tabs"), + SettingsPageItem::SettingItem(SettingItem { + title: "Preview Tabs Enabled", + description: "Whether to show opened editors as preview tabs", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(preview_tabs) = &settings_content.preview_tabs { + &preview_tabs.enabled + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .preview_tabs + .get_or_insert_default() + .enabled + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Enable Preview From File Finder", + description: "Whether to open tabs in preview mode when selected from the file finder", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(preview_tabs) = &settings_content.preview_tabs { + &preview_tabs.enable_preview_from_file_finder + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .preview_tabs + .get_or_insert_default() + .enable_preview_from_file_finder + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Enable Preview From Code Navigation", + description: "Whether a preview tab gets replaced when code navigation is used to navigate away from the tab", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(preview_tabs) = &settings_content.preview_tabs { + &preview_tabs.enable_preview_from_code_navigation + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .preview_tabs + .get_or_insert_default() + .enable_preview_from_code_navigation + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "Version Control", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Git"), + SettingsPageItem::SettingItem(SettingItem { + title: "Git Gutter", + description: "Control whether the git gutter is shown", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + &git.git_gutter + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.git.get_or_insert_default().git_gutter + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Gutter Debounce", + // description: "Debounce threshold in milliseconds after which changes are reflected in the git gutter", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(git) = &settings_content.git { + // &git.gutter_debounce + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content.git.get_or_insert_default().gutter_debounce + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Inline Blame Enabled", + description: "Whether or not to show git blame data inline in the currently focused line", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + if let Some(inline_blame) = &git.inline_blame { + &inline_blame.enabled + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git + .get_or_insert_default() + .inline_blame + .get_or_insert_default() + .enabled + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Commit Summary", + description: "Whether to show commit summary as part of the inline blame", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + if let Some(inline_blame) = &git.inline_blame { + &inline_blame.show_commit_summary + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git + .get_or_insert_default() + .inline_blame + .get_or_insert_default() + .show_commit_summary + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Avatar", + description: "Whether to show the avatar of the author of the commit", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + if let Some(blame) = &git.blame { + &blame.show_avatar + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git + .get_or_insert_default() + .blame + .get_or_insert_default() + .show_avatar + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Author Name In Branch Picker", + description: "Whether to show author name as part of the commit information in branch picker", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + if let Some(branch_picker) = &git.branch_picker { + &branch_picker.show_author_name + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git + .get_or_insert_default() + .branch_picker + .get_or_insert_default() + .show_author_name + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Hunk Style", + description: "How git hunks are displayed visually in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git) = &settings_content.git { + &git.hunk_style + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.git.get_or_insert_default().hunk_style + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "System & Network", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Network"), + // todo(settings_ui): Proxy needs a default + // SettingsPageItem::SettingItem(SettingItem { + // title: "Proxy", + // description: "The proxy to use for network requests", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.proxy, + // pick_mut: |settings_content| &mut settings_content.proxy, + // }), + // metadata: Some(Box::new(SettingsFieldMetadata { + // placeholder: Some("socks5h://localhost:10808"), + // })), + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Server URL", + description: "The URL of the Zed server to connect to", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.server_url, + pick_mut: |settings_content| &mut settings_content.server_url, + }), + metadata: Some(Box::new(SettingsFieldMetadata { + placeholder: Some("https://zed.dev"), + })), + }), + SettingsPageItem::SectionHeader("System"), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Update", + description: "Whether or not to automatically check for updates", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.auto_update, + pick_mut: |settings_content| &mut settings_content.auto_update, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "Diagnostics & Errors", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("Display"), + SettingsPageItem::SettingItem(SettingItem { + title: "Diagnostics Button", + description: "Whether to show the project diagnostics button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(diagnostics) = &settings_content.diagnostics { + &diagnostics.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.diagnostics.get_or_insert_default().button + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Filtering"), + SettingsPageItem::SettingItem(SettingItem { + title: "Max Severity", + description: "Which level to use to filter out diagnostics displayed in the editor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.diagnostics_max_severity, + pick_mut: |settings_content| { + &mut settings_content.editor.diagnostics_max_severity + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Include Warnings", + description: "Whether to show warnings or not by default", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(diagnostics) = &settings_content.diagnostics { + &diagnostics.include_warnings + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .diagnostics + .get_or_insert_default() + .include_warnings + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Inline"), + SettingsPageItem::SettingItem(SettingItem { + title: "Inline Diagnostics Enabled", + description: "Whether to show diagnostics inline or not", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(diagnostics) = &settings_content.diagnostics { + if let Some(inline) = &diagnostics.inline { + &inline.enabled + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .diagnostics + .get_or_insert_default() + .inline + .get_or_insert_default() + .enabled + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Inline Update Debounce", + // description: "The delay in milliseconds to show inline diagnostics after the last diagnostic update", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(diagnostics) = &settings_content.diagnostics { + // if let Some(inline) = &diagnostics.inline { + // &inline.update_debounce_ms + // } else { + // &None + // } + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .diagnostics + // .get_or_insert_default() + // .inline + // .get_or_insert_default() + // .update_debounce_ms + // }, + // }), + // metadata: None, + // }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Inline Padding", + // description: "The amount of padding between the end of the source line and the start of the inline diagnostic", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(diagnostics) = &settings_content.diagnostics { + // if let Some(inline) = &diagnostics.inline { + // &inline.padding + // } else { + // &None + // } + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .diagnostics + // .get_or_insert_default() + // .inline + // .get_or_insert_default() + // .padding + // }, + // }), + // metadata: None, + // }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Inline Min Column", + // description: "The minimum column to display inline diagnostics", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(diagnostics) = &settings_content.diagnostics { + // if let Some(inline) = &diagnostics.inline { + // &inline.min_column + // } else { + // &None + // } + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .diagnostics + // .get_or_insert_default() + // .inline + // .get_or_insert_default() + // .min_column + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Performance"), + SettingsPageItem::SettingItem(SettingItem { + title: "LSP Pull Diagnostics Enabled", + description: "Whether to pull for diagnostics or not", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(diagnostics) = &settings_content.diagnostics { + if let Some(lsp_pull) = &diagnostics.lsp_pull_diagnostics { + &lsp_pull.enabled + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .diagnostics + .get_or_insert_default() + .lsp_pull_diagnostics + .get_or_insert_default() + .enabled + }, + }), + metadata: None, + }), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "LSP Pull Debounce", + // description: "Minimum time to wait before pulling diagnostics from the language server(s)", + // field: Box::new(SettingField { + // pick: |settings_content| { + // if let Some(diagnostics) = &settings_content.diagnostics { + // if let Some(lsp_pull) = &diagnostics.lsp_pull_diagnostics { + // &lsp_pull.debounce_ms + // } else { + // &None + // } + // } else { + // &None + // } + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .diagnostics + // .get_or_insert_default() + // .lsp_pull_diagnostics + // .get_or_insert_default() + // .debounce_ms + // }, + // }), + // metadata: None, + // }), + ], + }, + SettingsPage { + title: "Collaboration", + expanded: false, items: vec![ - SettingsPageItem::SectionHeader("General"), + SettingsPageItem::SectionHeader("Calls"), SettingsPageItem::SettingItem(SettingItem { - title: "Confirm Quit", - description: "Whether to confirm before quitting Zed", + title: "Mute On Join", + description: "Whether the microphone should be muted when joining a channel or a call", field: Box::new(SettingField { - pick: |settings_content| &settings_content.workspace.confirm_quit, - pick_mut: |settings_content| &mut settings_content.workspace.confirm_quit, + pick: |settings_content| { + if let Some(calls) = &settings_content.calls { + &calls.mute_on_join + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.calls.get_or_insert_default().mute_on_join + }, }), metadata: None, }), SettingsPageItem::SettingItem(SettingItem { - title: "Auto Update", - description: "Automatically update Zed (may be ignored on Linux if installed through a package manager)", + title: "Share On Join", + description: "Whether your current project should be shared when joining an empty channel", field: Box::new(SettingField { - pick: |settings_content| &settings_content.auto_update, - pick_mut: |settings_content| &mut settings_content.auto_update, + pick: |settings_content| { + if let Some(calls) = &settings_content.calls { + &calls.share_on_join + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.calls.get_or_insert_default().share_on_join + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Panel"), + SettingsPageItem::SettingItem(SettingItem { + title: "Collaboration Panel Button", + description: "Whether to show the collaboration panel button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(collab) = &settings_content.collaboration_panel { + &collab.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .collaboration_panel + .get_or_insert_default() + .button + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Experimental"), + SettingsPageItem::SettingItem(SettingItem { + title: "Rodio Audio", + description: "Opt into the new audio system", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(audio) = &settings_content.audio { + &audio.rodio_audio + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.audio.get_or_insert_default().rodio_audio + }, + }), + metadata: None, + }), + ], + }, + SettingsPage { + title: "AI", + expanded: false, + items: vec![ + SettingsPageItem::SectionHeader("General"), + SettingsPageItem::SettingItem(SettingItem { + title: "Disable AI", + description: "Whether to disable all AI features in Zed", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.disable_ai, + pick_mut: |settings_content| &mut settings_content.disable_ai, }), metadata: None, }), - SettingsPageItem::SectionHeader("Privacy"), ], }, + ] +} + +// Derive Macro, on the new ProjectSettings struct + +fn project_settings_data() -> Vec { + vec![ SettingsPage { title: "Project", - expanded: true, + expanded: false, items: vec![ SettingsPageItem::SectionHeader("Worktree Settings Content"), SettingsPageItem::SettingItem(SettingItem { @@ -184,32 +2211,352 @@ fn user_settings_data() -> Vec { ], }, SettingsPage { - title: "AI", - expanded: true, + title: "Appearance & Behavior", + expanded: false, items: vec![ - SettingsPageItem::SectionHeader("General"), + SettingsPageItem::SectionHeader("Guides"), SettingsPageItem::SettingItem(SettingItem { - title: "Disable AI", - description: "Whether to disable all AI features in Zed", + title: "Show Wrap Guides", + description: "Whether to show wrap guides (vertical rulers)", field: Box::new(SettingField { - pick: |settings_content| &settings_content.disable_ai, - pick_mut: |settings_content| &mut settings_content.disable_ai, + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_wrap_guides + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_wrap_guides + }, + }), + metadata: None, + }), + // todo(settings_ui): This needs a custom component + // SettingsPageItem::SettingItem(SettingItem { + // title: "Wrap Guides", + // description: "Character counts at which to show wrap guides", + // field: Box::new(SettingField { + // pick: |settings_content| { + // &settings_content + // .project + // .all_languages + // .defaults + // .wrap_guides + // }, + // pick_mut: |settings_content| { + // &mut settings_content + // .project + // .all_languages + // .defaults + // .wrap_guides + // }, + // }), + // metadata: None, + // }), + SettingsPageItem::SectionHeader("Whitespace"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Whitespace", + description: "Whether to show tabs and spaces", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_whitespaces + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_whitespaces + }, }), metadata: None, }), ], }, SettingsPage { - title: "Appearance & Behavior", - expanded: true, + title: "Editing", + expanded: false, items: vec![ - SettingsPageItem::SectionHeader("Cursor"), + SettingsPageItem::SectionHeader("Indentation"), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Tab Size", + // description: "How many columns a tab should occupy", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.project.all_languages.defaults.tab_size, + // pick_mut: |settings_content| &mut settings_content.project.all_languages.defaults.tab_size, + // }), + // metadata: None, + // }), SettingsPageItem::SettingItem(SettingItem { - title: "Cursor Shape", - description: "Cursor shape for the editor", + title: "Hard Tabs", + description: "Whether to indent lines using tab characters, as opposed to multiple spaces", field: Box::new(SettingField { - pick: |settings_content| &settings_content.editor.cursor_shape, - pick_mut: |settings_content| &mut settings_content.editor.cursor_shape, + pick: |settings_content| { + &settings_content.project.all_languages.defaults.hard_tabs + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.hard_tabs + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Indent", + description: "Whether indentation should be adjusted based on the context whilst typing", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.defaults.auto_indent + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.auto_indent + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Indent On Paste", + description: "Whether indentation of pasted content should be adjusted based on the context", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .auto_indent_on_paste + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .auto_indent_on_paste + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Wrapping"), + // todo(settings_ui): Needs numeric stepper + // SettingsPageItem::SettingItem(SettingItem { + // title: "Preferred Line Length", + // description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled", + // field: Box::new(SettingField { + // pick: |settings_content| &settings_content.project.all_languages.defaults.preferred_line_length, + // pick_mut: |settings_content| &mut settings_content.project.all_languages.defaults.preferred_line_length, + // }), + // metadata: None, + // }), + SettingsPageItem::SettingItem(SettingItem { + title: "Soft Wrap", + description: "How to soft-wrap long lines of text", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.defaults.soft_wrap + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.defaults.soft_wrap + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Auto Actions"), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Autoclose", + description: "Whether to automatically type closing characters for you", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_autoclose + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_autoclose + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Auto Surround", + description: "Whether to automatically surround text with characters for you", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_auto_surround + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_auto_surround + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use On Type Format", + description: "Whether to use additional LSP queries to format the code after every trigger symbol input", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .use_on_type_format + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .use_on_type_format + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Always Treat Brackets As Autoclosed", + description: "Controls how the editor handles the autoclosed characters", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .always_treat_brackets_as_autoclosed + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .always_treat_brackets_as_autoclosed + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Formatting"), + SettingsPageItem::SettingItem(SettingItem { + title: "Remove Trailing Whitespace On Save", + description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .remove_trailing_whitespace_on_save + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .remove_trailing_whitespace_on_save + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Ensure Final Newline On Save", + description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .ensure_final_newline_on_save + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .ensure_final_newline_on_save + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Extend Comment On Newline", + description: "Whether to start a new line with a comment when a previous line is a comment as well", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .extend_comment_on_newline + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .extend_comment_on_newline + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Completions"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Completions On Input", + description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_completions_on_input + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_completions_on_input + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Show Completion Documentation", + description: "Whether to display inline and alongside documentation for items in the completions menu", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content + .project + .all_languages + .defaults + .show_completion_documentation + }, + pick_mut: |settings_content| { + &mut settings_content + .project + .all_languages + .defaults + .show_completion_documentation + }, }), metadata: None, }), @@ -218,31 +2565,6 @@ fn user_settings_data() -> Vec { ] } -// Derive Macro, on the new ProjectSettings struct - -fn project_settings_data() -> Vec { - vec![SettingsPage { - title: "Project", - expanded: true, - items: vec![ - SettingsPageItem::SectionHeader("Worktree Settings Content"), - SettingsPageItem::SettingItem(SettingItem { - title: "Project Name", - description: "The displayed name of this project. If not set, the root directory name", - field: Box::new(SettingField { - pick: |settings_content| &settings_content.project.worktree.project_name, - pick_mut: |settings_content| { - &mut settings_content.project.worktree.project_name - }, - }), - metadata: Some(Box::new(SettingsFieldMetadata { - placeholder: Some("A new name"), - })), - }), - ], - }] -} - pub struct SettingsUiFeatureFlag; impl FeatureFlag for SettingsUiFeatureFlag { @@ -297,7 +2619,97 @@ fn init_renderers(cx: &mut App) { }) .add_renderer::(|settings_field, file, _, window, cx| { render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, metadata, _, cx| { + // todo(settings_ui): We need to pass in a validator for this to ensure that users that type in invalid font names + render_text_field(settings_field.clone(), file, metadata, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + // todo(settings_ui): Do we want to expose the custom variant of buffer line height? + // right now there's a manual impl of strum::VariantArray + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::( + |settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }, + ) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::( + |settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }, + ) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_dropdown(*settings_field, file, window, cx) }); + + // todo(settings_ui): Figure out how we want to handle discriminant unions + // .add_renderer::(|settings_field, file, _, window, cx| { + // render_dropdown(*settings_field, file, window, cx) + // }); } pub fn open_settings_editor(cx: &mut App) -> anyhow::Result> { @@ -826,16 +3238,23 @@ impl SettingsWindow { }) } - fn render_page(&self, window: &mut Window, cx: &mut Context) -> Div { + fn render_page(&self, window: &mut Window, cx: &mut Context) -> Stateful
{ let items: Vec<_> = self.page_items().collect(); let items_len = items.len(); v_flex() + .id("settings-ui-page") .gap_4() .children(items.into_iter().enumerate().map(|(index, item)| { let is_last = index == items_len - 1; item.render(self.current_file.clone(), is_last, window, cx) })) + .overflow_y_scroll() + .track_scroll( + window + .use_state(cx, |_, _| ScrollHandle::default()) + .read(cx), + ) } fn current_page_index(&self) -> usize { @@ -889,22 +3308,20 @@ 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, +fn render_text_field + Into + AsRef + Clone>( + field: SettingField, file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, cx: &mut App, ) -> AnyElement { 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()); + let initial_text = Some(initial_text.clone()).filter(|s| !s.as_ref().is_empty()); SettingsEditor::new() - .when_some(initial_text, |editor, text| editor.with_initial_text(text)) + .when_some(initial_text, |editor, text| { + editor.with_initial_text(text.into()) + }) .when_some( metadata.and_then(|metadata| metadata.placeholder), |editor, placeholder| editor.with_placeholder(placeholder), @@ -912,7 +3329,7 @@ fn render_text_field( .on_confirm(move |new_text, cx: &mut App| { cx.update_global(move |store: &mut SettingsStore, cx| { store.update_settings_file(::global(cx), move |settings, _cx| { - *(field.pick_mut)(settings) = new_text; + *(field.pick_mut)(settings) = new_text.map(Into::into); }); }); }) @@ -933,6 +3350,7 @@ fn render_toggle_button + From + Copy>( }; Switch::new("toggle_button", toggle_state) + .color(ui::SwitchColor::Accent) .on_click({ move |state, _window, cx| { let state = *state == ui::ToggleState::Selected;