diff --git a/assets/settings/default.json b/assets/settings/default.json index a77ce82f00b746adf9efa3886474f4d0fe53847c..5d195f4bcd275796d942aac69f5a3cde40f9709d 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1233,8 +1233,8 @@ "git_gutter": "tracked_files", /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. /// - /// Default: null - "gutter_debounce": null, + /// Default: 0 + "gutter_debounce": 0, // Control whether the git blame information is shown inline, // in the currently focused line. "inline_blame": { diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 3426171e03f70b18faa71584429a49ea009f73c4..e561522b1e323a8739b46fd08dcd9321321d46bd 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -267,7 +267,7 @@ impl Settings for EditorSettings { delay: drag_and_drop_selection.delay.unwrap(), }, lsp_document_colors: editor.lsp_document_colors.unwrap(), - minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap(), + minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap().0, } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7e6bb35c0ead718d61aba2dae73999f54c0498ce..66b5702f36f462d0732eccbb9a249c5996e53a24 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3252,9 +3252,9 @@ impl Project { self.buffers_needing_diff.insert(buffer.downgrade()); let first_insertion = self.buffers_needing_diff.len() == 1; let settings = ProjectSettings::get_global(cx); - let delay = if let Some(delay) = settings.git.gutter_debounce { - delay - } else { + let delay = settings.git.gutter_debounce; + + if delay == 0 { if first_insertion { let this = cx.weak_entity(); cx.defer(move |cx| { @@ -3266,7 +3266,7 @@ impl Project { }); } return; - }; + } const MIN_DELAY: u64 = 50; let delay = delay.max(MIN_DELAY); diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 5bae7b05773afb71ae9bcb541ef140cda29464ca..df9aa833d6fdacc93b09eea1b465ebe70503772f 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -300,8 +300,8 @@ pub struct GitSettings { pub git_gutter: settings::GitGutterSetting, /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. /// - /// Default: null - pub gutter_debounce: Option, + /// Default: 0 + pub gutter_debounce: u64, /// Whether or not to show git blame data inline in /// the currently focused line. /// @@ -446,7 +446,7 @@ impl Settings for ProjectSettings { let git = content.git.as_ref().unwrap(); let git_settings = GitSettings { git_gutter: git.git_gutter.unwrap(), - gutter_debounce: git.gutter_debounce, + gutter_debounce: git.gutter_debounce.unwrap_or_default(), inline_blame: { let inline = git.inline_blame.unwrap(); InlineBlameSettings { diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index befcbc4d780533b4a6d5805dddb2214694ff1d8e..bb7a972310bfdf4fa7070bbac53b579fc6bcbb93 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -383,7 +383,18 @@ pub struct DebuggerSettingsContent { /// The granularity of one 'step' in the stepping requests `next`, `stepIn`, `stepOut`, and `stepBack`. #[derive( - PartialEq, Eq, Debug, Hash, Clone, Copy, Deserialize, Serialize, JsonSchema, MergeFrom, + PartialEq, + Eq, + Debug, + Hash, + Clone, + Copy, + Deserialize, + Serialize, + JsonSchema, + MergeFrom, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum SteppingGranularity { diff --git a/crates/settings/src/settings_content/editor.rs b/crates/settings/src/settings_content/editor.rs index 79a034e75ccbfbabf53f4edbe9bd923325c2fb3c..5bd1b0daf9206b6ea97374a0281c7f737e0fc2e0 100644 --- a/crates/settings/src/settings_content/editor.rs +++ b/crates/settings/src/settings_content/editor.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::num; use collections::HashMap; @@ -153,7 +154,8 @@ pub struct EditorSettingsContent { /// /// Values range from 0 to 106. Set to 0 to disable adjustments. /// Default: 45 - pub minimum_contrast_for_highlights: Option, + #[schemars(range(min = 0, max = 106))] + pub minimum_contrast_for_highlights: Option, /// Whether to follow-up empty go to definition responses from the language server or not. /// `FindAllReferences` allows to look up references of the same symbol instead. @@ -425,7 +427,18 @@ pub enum DoubleClickInMultibuffer { /// /// Default: always #[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 MinimapThumb { @@ -440,7 +453,18 @@ pub enum MinimapThumb { /// /// Default: left_open #[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 MinimapThumbBorder { @@ -460,7 +484,19 @@ pub enum MinimapThumbBorder { /// Which diagnostic indicators to show in the scrollbar. /// /// Default: all -#[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 = "lowercase")] pub enum ScrollbarDiagnostics { /// Show all diagnostic levels: hint, information, warnings, error. @@ -682,7 +718,18 @@ pub struct DragAndDropSelectionContent { /// /// Default: never #[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 ShowMinimap { @@ -699,7 +746,18 @@ pub enum ShowMinimap { /// /// Default: all_editors #[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 DisplayIn { @@ -709,3 +767,28 @@ pub enum DisplayIn { #[default] ActiveEditor, } + +/// Minimum APCA perceptual contrast for text over highlight backgrounds. +/// +/// Valid range: 0.0 to 106.0 +/// Default: 45.0 +#[derive( + Clone, + Copy, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + PartialOrd, + derive_more::FromStr, +)] +#[serde(transparent)] +pub struct MinimumContrast(pub f32); + +impl Display for MinimumContrast { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:.1}", self.0) + } +} diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs index 81398e593df92ad6d06dba411a108dc5f4b7c65c..1776f7418183b024fbaeca75fd311ccfa9d9481a 100644 --- a/crates/settings/src/settings_content/project.rs +++ b/crates/settings/src/settings_content/project.rs @@ -249,7 +249,7 @@ pub struct GitSettings { pub git_gutter: Option, /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. /// - /// Default: null + /// Default: 0 pub gutter_debounce: Option, /// Whether or not to show git blame data inline in /// the currently focused line. diff --git a/crates/settings/src/settings_content/terminal.rs b/crates/settings/src/settings_content/terminal.rs index 9b7813c55b3b1ba9be56c13dfdaf7dbec13cd20a..1154c1cd6c9a89cba8e4f7a05278fd6ad8d02cff 100644 --- a/crates/settings/src/settings_content/terminal.rs +++ b/crates/settings/src/settings_content/terminal.rs @@ -199,7 +199,19 @@ impl TerminalLineHeight { /// When to show the scrollbar. /// /// Default: auto -#[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 ShowScrollbar { /// Show the scrollbar if there's important information or diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index b94a24866c76a57328f3b1ebf6a688b2fa4b3820..ab65a43c011696f54e9c4875fe68d70897866046 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -538,6 +538,7 @@ impl SettingsStore { &self, target_file: SettingsFile, pick: fn(&SettingsContent) -> &Option, + type_name: &'static str, ) -> (SettingsFile, &T) { // TODO: Add a metadata field for overriding the "overrides" tag, for contextually different settings // e.g. disable AI isn't overridden, or a vec that gets extended instead or some such @@ -568,7 +569,7 @@ impl SettingsStore { } } - unreachable!("All values should have defaults"); + unreachable!("{type_name}: doesn't have a default value"); } } @@ -1714,16 +1715,24 @@ mod tests { let default_value = get(&store.default_settings).unwrap(); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local.clone()), get), + store.get_value_from_file( + SettingsFile::Local(local.clone()), + get, + "preferred line length" + ), (SettingsFile::User, &0) ); assert_eq!( - store.get_value_from_file(SettingsFile::User, get), + store.get_value_from_file(SettingsFile::User, get, "preferred line length"), (SettingsFile::User, &0) ); store.set_user_settings(r#"{}"#, cx).unwrap(); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local.clone()), get), + store.get_value_from_file( + SettingsFile::Local(local.clone()), + get, + "preferred line length" + ), (SettingsFile::Default, &default_value) ); store @@ -1736,11 +1745,15 @@ mod tests { ) .unwrap(); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local.clone()), get), + store.get_value_from_file( + SettingsFile::Local(local.clone()), + get, + "preferred line length" + ), (SettingsFile::Local(local), &80) ); assert_eq!( - store.get_value_from_file(SettingsFile::User, get), + store.get_value_from_file(SettingsFile::User, get, "preferred line length"), (SettingsFile::Default, &default_value) ); } @@ -1817,11 +1830,19 @@ mod tests { // each local child should only inherit from it's parent assert_eq!( - store.get_value_from_file(SettingsFile::Local(local_2_child), get), + store.get_value_from_file( + SettingsFile::Local(local_2_child), + get, + "preferred_line_length" + ), (SettingsFile::Local(local_2), &2) ); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local_1_child.clone()), get), + store.get_value_from_file( + SettingsFile::Local(local_1_child.clone()), + get, + "preferred_line_length" + ), (SettingsFile::Local(local_1.clone()), &1) ); @@ -1847,7 +1868,11 @@ mod tests { .unwrap(); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local_1_adjacent_child.clone()), get), + store.get_value_from_file( + SettingsFile::Local(local_1_adjacent_child.clone()), + get, + "preferred_line_length" + ), (SettingsFile::Local(local_1.clone()), &1) ); store @@ -1869,7 +1894,11 @@ mod tests { ) .unwrap(); assert_eq!( - store.get_value_from_file(SettingsFile::Local(local_1_child), get), + store.get_value_from_file( + SettingsFile::Local(local_1_child), + get, + "preferred_line_length" + ), (SettingsFile::Local(local_1), &1) ); } diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index ed554079d3706bc9d405b57e4319210c93c6edc7..00fdbfb035656c265992cb2d1a734bcc49a01bfb 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -712,6 +712,41 @@ pub(crate) fn user_settings_data() -> Vec { }), metadata: None, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Expand Excerpt Lines", + description: "How many lines to expand the multibuffer excerpts by default", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.expand_excerpt_lines, + pick_mut: |settings_content| { + &mut settings_content.editor.expand_excerpt_lines + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Excerpt Context Lines", + description: "How many lines of context to provide in multibuffer excerpts by default", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.excerpt_context_lines, + pick_mut: |settings_content| { + &mut settings_content.editor.excerpt_context_lines + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Minimum Contrast For Highlights", + description: "The minimum APCA perceptual contrast to maintain when rendering text over highlight backgrounds", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.editor.minimum_contrast_for_highlights + }, + pick_mut: |settings_content| { + &mut settings_content.editor.minimum_contrast_for_highlights + }, + }), + metadata: None, + }), SettingsPageItem::SectionHeader("Scrolling"), SettingsPageItem::SettingItem(SettingItem { title: "Scroll Beyond Last Line", @@ -1181,6 +1216,354 @@ pub(crate) fn user_settings_data() -> Vec { }), metadata: None, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Min Line Number Digits", + description: "Minimum number of characters to reserve space for in the gutter.", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(gutter) = &settings_content.editor.gutter { + &gutter.min_line_number_digits + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .gutter + .get_or_insert_default() + .min_line_number_digits + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Scrollbar"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show", + description: "When to show the scrollbar in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.show + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .show + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Cursors", + description: "Whether to show cursor positions in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.cursors + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .cursors + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Git Diff", + description: "Whether to show git diff indicators in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.git_diff + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .git_diff + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Search Results", + description: "Whether to show buffer search result indicators in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.search_results + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .search_results + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Selected Text", + description: "Whether to show selected text occurrences in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.selected_text + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .selected_text + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Selected Symbol", + description: "Whether to show selected symbol occurrences in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.selected_symbol + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .selected_symbol + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Diagnostics", + description: "Which diagnostic indicators to show in the scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + &scrollbar.diagnostics + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .diagnostics + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Horizontal Scrollbar", + description: "When false, forcefully disables the horizontal scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + if let Some(axes) = &scrollbar.axes { + &axes.horizontal + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .axes + .get_or_insert_default() + .horizontal + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Vertical Scrollbar", + description: "When false, forcefully disables the vertical scrollbar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(scrollbar) = &settings_content.editor.scrollbar { + if let Some(axes) = &scrollbar.axes { + &axes.vertical + } else { + &None + } + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .scrollbar + .get_or_insert_default() + .axes + .get_or_insert_default() + .vertical + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Minimap"), + SettingsPageItem::SettingItem(SettingItem { + title: "Show", + description: "When to show the minimap in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap { + &minimap.show + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.editor.minimap.get_or_insert_default().show + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Display In", + description: "Where to show the minimap in the editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap { + &minimap.display_in + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .minimap + .get_or_insert_default() + .display_in + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Thumb", + description: "When to show the minimap thumb", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap { + &minimap.thumb + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .minimap + .get_or_insert_default() + .thumb + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Thumb Border", + description: "Border style for the minimap's scrollbar thumb", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap { + &minimap.thumb_border + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .minimap + .get_or_insert_default() + .thumb_border + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Current Line Highlight", + description: "How to highlight the current line in the minimap", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap + && minimap.current_line_highlight.is_some() + { + &minimap.current_line_highlight + } else { + &settings_content.editor.current_line_highlight + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .minimap + .get_or_insert_default() + .current_line_highlight + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Max Width Columns", + description: "Maximum number of columns to display in the minimap", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(minimap) = &settings_content.editor.minimap { + &minimap.max_width_columns + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .editor + .minimap + .get_or_insert_default() + .max_width_columns + }, + }), + metadata: None, + }), SettingsPageItem::SectionHeader("Tabs"), SettingsPageItem::SettingItem(SettingItem { title: "Show Tab Bar", @@ -1253,6 +1636,8 @@ pub(crate) fn user_settings_data() -> Vec { // SettingsPageItem::SettingItem(SettingItem { // title: "Maximum Tabs", // description: "Maximum open tabs in a pane. Will not close an unsaved tab", + // // todo(settings_ui): The default for this value is null and it's use in code + // // is complex, so I'm going to come back to this later // field: Box::new(SettingField { // pick: |settings_content| &settings_content.workspace.max_tabs, // pick_mut: |settings_content| &mut settings_content.workspace.max_tabs, @@ -2593,6 +2978,61 @@ pub(crate) fn user_settings_data() -> Vec { // }), // metadata: None, // }), + SettingsPageItem::SectionHeader("Git Panel"), + SettingsPageItem::SettingItem(SettingItem { + title: "Button", + description: "Whether to show the git panel button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.git_panel.get_or_insert_default().button + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Dock", + description: "Where to dock the git panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.git_panel.get_or_insert_default().dock + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Default Width", + description: "Default width of the git panel in pixels", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.default_width + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .default_width + }, + }), + metadata: None, + }), SettingsPageItem::SectionHeader("Notification Panel"), SettingsPageItem::SettingItem(SettingItem { title: "Notification Panel Button", @@ -2742,23 +3182,23 @@ pub(crate) fn user_settings_data() -> Vec { metadata: None, }), // todo(settings_ui): Figure out the right default for this value in default.json - // 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: "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 Git Blame", description: "Whether or not to show git blame data inline in the currently focused line", @@ -3195,6 +3635,143 @@ pub(crate) fn user_settings_data() -> Vec { }), ], }, + SettingsPage { + title: "Debugger", + items: vec![ + SettingsPageItem::SectionHeader("General"), + SettingsPageItem::SettingItem(SettingItem { + title: "Stepping Granularity", + description: "Determines the stepping granularity for debug operations", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.stepping_granularity + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .debugger + .get_or_insert_default() + .stepping_granularity + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Save Breakpoints", + description: "Whether breakpoints should be reused across Zed sessions", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.save_breakpoints + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .debugger + .get_or_insert_default() + .save_breakpoints + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Timeout", + description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.timeout + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.debugger.get_or_insert_default().timeout + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Dock", + description: "The dock position of the debug panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.debugger.get_or_insert_default().dock + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Log DAP Communications", + description: "Whether to log messages between active debug adapters and Zed", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.log_dap_communications + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .debugger + .get_or_insert_default() + .log_dap_communications + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Format DAP Log Messages", + description: "Whether to format DAP messages when adding them to debug adapter logger", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.format_dap_log_messages + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .debugger + .get_or_insert_default() + .format_dap_log_messages + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Button", + description: "Whether to show the debug button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.debugger.get_or_insert_default().button + }, + }), + metadata: None, + }), + ], + }, SettingsPage { title: "Collaboration", items: vec![ @@ -3251,6 +3828,83 @@ pub(crate) fn user_settings_data() -> Vec { }), metadata: None, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Microphone Volume", + description: "Automatically adjust microphone volume (requires Rodio Audio)", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(audio) = &settings_content.audio { + &audio.auto_microphone_volume + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .audio + .get_or_insert_default() + .auto_microphone_volume + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Speaker Volume", + description: "Automatically adjust volume of other call members (requires Rodio Audio)", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(audio) = &settings_content.audio { + &audio.auto_speaker_volume + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .audio + .get_or_insert_default() + .auto_speaker_volume + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Denoise", + description: "Remove background noises (requires Rodio Audio)", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(audio) = &settings_content.audio { + &audio.denoise + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.audio.get_or_insert_default().denoise + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Legacy Audio Compatible", + description: "Use audio parameters compatible with previous versions (requires Rodio Audio)", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(audio) = &settings_content.audio { + &audio.legacy_audio_compatible + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .audio + .get_or_insert_default() + .legacy_audio_compatible + }, + }), + metadata: None, + }), ], }, SettingsPage { diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index d71743e2430cb3aed9b6b915f7c8066c8305cbfd..dd5d953c049542eceac224b17b0a9a66a04c4661 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -22,7 +22,7 @@ use std::{ any::{Any, TypeId, type_name}, cell::RefCell, collections::HashMap, - num::NonZeroU32, + num::{NonZero, NonZeroU32}, ops::Range, rc::Rc, sync::{Arc, LazyLock, RwLock, atomic::AtomicBool}, @@ -105,9 +105,11 @@ impl AnySettingField for SettingField { return file.to_settings(); } - let (file, _) = cx - .global::() - .get_value_from_file(file.to_settings(), self.pick); + let (file, _) = cx.global::().get_value_from_file( + file.to_settings(), + self.pick, + self.type_name(), + ); return file; } } @@ -381,6 +383,12 @@ fn init_renderers(cx: &mut App) { .add_renderer::(|settings_field, file, _, window, cx| { render_numeric_stepper(*settings_field, file, window, cx) }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_numeric_stepper(*settings_field, file, window, cx) + }) + .add_renderer::>(|settings_field, file, _, window, cx| { + render_numeric_stepper(*settings_field, file, window, cx) + }) .add_renderer::(|settings_field, file, _, window, cx| { render_numeric_stepper(*settings_field, file, window, cx) }) @@ -389,6 +397,30 @@ fn init_renderers(cx: &mut App) { }) .add_renderer::(|settings_field, file, _, window, cx| { render_numeric_stepper(*settings_field, file, window, cx) + }) + .add_renderer::(|settings_field, file, _, window, cx| { + render_numeric_stepper(*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 @@ -1439,8 +1471,11 @@ fn render_text_field + Into + AsRef + Clone>( 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) = SettingsStore::global(cx).get_value_from_file( + file.to_settings(), + field.pick, + field.type_name(), + ); let initial_text = Some(initial_text.clone()).filter(|s| !s.as_ref().is_empty()); SettingsEditor::new() @@ -1468,7 +1503,11 @@ fn render_toggle_button + From + Copy>( file: SettingsUiFile, cx: &mut App, ) -> AnyElement { - let (_, &value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); + let (_, &value) = SettingsStore::global(cx).get_value_from_file( + file.to_settings(), + field.pick, + field.type_name(), + ); let toggle_state = if value.into() { ToggleState::Selected @@ -1499,7 +1538,7 @@ fn render_font_picker( cx: &mut App, ) -> AnyElement { let current_value = SettingsStore::global(cx) - .get_value_from_file(file.to_settings(), field.pick) + .get_value_from_file(file.to_settings(), field.pick, field.type_name()) .1 .clone(); @@ -1556,7 +1595,11 @@ fn render_numeric_stepper( window: &mut Window, cx: &mut App, ) -> AnyElement { - let (_, &value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); + let (_, &value) = SettingsStore::global(cx).get_value_from_file( + file.to_settings(), + field.pick, + field.type_name(), + ); NumericStepper::new("numeric_stepper", value, window, cx) .on_change({ @@ -1585,8 +1628,11 @@ where let variants = || -> &'static [T] { ::VARIANTS }; let labels = || -> &'static [&'static str] { ::VARIANTS }; - let (_, ¤t_value) = - SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); + let (_, ¤t_value) = SettingsStore::global(cx).get_value_from_file( + file.to_settings(), + field.pick, + field.type_name(), + ); let current_value_label = labels()[variants().iter().position(|v| *v == current_value).unwrap()]; diff --git a/crates/ui_input/src/numeric_stepper.rs b/crates/ui_input/src/numeric_stepper.rs index 1ae13d4957124cdde957a6f12afb7d48c0b3bddd..436f67d654c4f5f1e5648363de673402c968ea70 100644 --- a/crates/ui_input/src/numeric_stepper.rs +++ b/crates/ui_input/src/numeric_stepper.rs @@ -1,6 +1,6 @@ use std::{ fmt::Display, - num::{NonZeroU32, NonZeroU64}, + num::{NonZero, NonZeroU32, NonZeroU64}, rc::Rc, str::FromStr, }; @@ -8,7 +8,7 @@ use std::{ use editor::{Editor, EditorStyle}; use gpui::{ClickEvent, Entity, FocusHandle, Focusable, FontWeight, Modifiers}; -use settings::CodeFade; +use settings::{CodeFade, MinimumContrast}; use ui::{IconButtonShape, prelude::*}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] @@ -88,6 +88,30 @@ impl NumericStepperType for settings::CodeFade { } } +impl NumericStepperType for settings::MinimumContrast { + fn default_step() -> Self { + MinimumContrast(1.0) + } + fn large_step() -> Self { + MinimumContrast(10.0) + } + fn small_step() -> Self { + MinimumContrast(0.5) + } + fn min_value() -> Self { + MinimumContrast(0.0) + } + fn max_value() -> Self { + MinimumContrast(106.0) + } + fn saturating_add(self, rhs: Self) -> Self { + MinimumContrast((self.0 + rhs.0).min(Self::max_value().0)) + } + fn saturating_sub(self, rhs: Self) -> Self { + MinimumContrast((self.0 - rhs.0).max(Self::min_value().0)) + } +} + macro_rules! impl_numeric_stepper_int { ($type:ident) => { impl NumericStepperType for $type { @@ -207,6 +231,7 @@ impl_numeric_stepper_int!(u64); impl_numeric_stepper_nonzero_int!(NonZeroU32, u32); impl_numeric_stepper_nonzero_int!(NonZeroU64, u64); +impl_numeric_stepper_nonzero_int!(NonZero, usize); #[derive(RegisterComponent)] pub struct NumericStepper {