diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 6c87ec5b51..0e23a69f07 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -269,7 +269,7 @@ async fn test_first_line_pattern(cx: &mut TestAppContext) { async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext) { cx.update(|cx| { init_settings(cx, |settings| { - settings.file_types.extend([ + settings.file_types.get_or_insert_default().extend([ ("TypeScript".into(), vec!["js".into()].into()), ( "JavaScript".into(), diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index ad6a22c620..829be3bbd4 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -639,7 +639,7 @@ impl settings::Settings for AllLanguageSettings { let mut file_types: FxHashMap, GlobSet> = FxHashMap::default(); - for (language, patterns) in &all_languages.file_types { + for (language, patterns) in all_languages.file_types.iter().flatten() { let mut builder = GlobSetBuilder::new(); for pattern in &patterns.0 { @@ -778,6 +778,7 @@ impl settings::Settings for AllLanguageSettings { .project .all_languages .file_types + .get_or_insert_default() .extend(associations); // cursor global ignore list applies to cursor-tab, so transfer it to edit_predictions.disabled_globs diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index 3599ac4110..92493557ee 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -234,7 +234,6 @@ impl UserSettingsContent { Eq, Default, strum::VariantArray, - strum::VariantNames, )] pub enum BaseKeymapContent { #[default] @@ -248,6 +247,19 @@ pub enum BaseKeymapContent { None, } +impl strum::VariantNames for BaseKeymapContent { + const VARIANTS: &'static [&'static str] = &[ + "VSCode", + "JetBrains", + "Sublime Text", + "Atom", + "TextMate", + "Emacs", + "Cursor", + "None", + ]; +} + #[skip_serializing_none] #[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)] pub struct TitleBarSettingsContent { @@ -502,7 +514,18 @@ pub struct GitPanelSettingsContent { } #[derive( - Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, + Default, + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + strum::VariantArray, + strum::VariantNames, )] #[serde(rename_all = "snake_case")] pub enum StatusStyle { @@ -512,7 +535,9 @@ pub enum StatusStyle { } #[skip_serializing_none] -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive( + Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, +)] pub struct ScrollbarSettings { pub show: Option, } @@ -814,7 +839,19 @@ pub struct ImageViewerSettingsContent { } #[skip_serializing_none] -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)] +#[derive( + Clone, + Copy, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + Default, + PartialEq, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum ImageFileSizeUnit { /// Displays file size in binary units (e.g., KiB, MiB). diff --git a/crates/settings/src/settings_content/editor.rs b/crates/settings/src/settings_content/editor.rs index 5bd1b0daf9..171cb874fc 100644 --- a/crates/settings/src/settings_content/editor.rs +++ b/crates/settings/src/settings_content/editor.rs @@ -334,7 +334,18 @@ pub struct GutterContent { /// How to render LSP `textDocument/documentColor` colors in the editor. #[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 DocumentColorsRenderMode { diff --git a/crates/settings/src/settings_content/language.rs b/crates/settings/src/settings_content/language.rs index d0f849cabb..0e3f29264b 100644 --- a/crates/settings/src/settings_content/language.rs +++ b/crates/settings/src/settings_content/language.rs @@ -27,8 +27,7 @@ pub struct AllLanguageSettingsContent { pub languages: LanguageToSettingsMap, /// Settings for associating file extensions and filenames /// with languages. - #[serde(default)] - pub file_types: HashMap, ExtendingVec>, + pub file_types: Option, ExtendingVec>>, } impl merge_from::MergeFrom for AllLanguageSettingsContent { diff --git a/crates/settings/src/settings_content/workspace.rs b/crates/settings/src/settings_content/workspace.rs index 65e1bec1c4..a560dc7c59 100644 --- a/crates/settings/src/settings_content/workspace.rs +++ b/crates/settings/src/settings_content/workspace.rs @@ -12,7 +12,7 @@ use crate::{DockPosition, DockSide, ScrollbarSettingsContent, ShowIndentGuides}; #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] pub struct WorkspaceSettingsContent { /// Active pane styling settings. - pub active_pane_modifiers: Option, + pub active_pane_modifiers: Option, /// Layout mode for the bottom dock /// /// Default: contained @@ -243,7 +243,7 @@ pub enum ActivateOnClose { #[skip_serializing_none] #[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)] #[serde(rename_all = "snake_case")] -pub struct ActivePanelModifiers { +pub struct ActivePaneModifiers { /// Size of the border surrounding the active pane. /// When set to 0, the active pane doesn't have any border. /// The border is drawn inset. @@ -403,14 +403,38 @@ impl AutosaveSetting { } } -#[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 PaneSplitDirectionHorizontal { Up, Down, } -#[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 PaneSplitDirectionVertical { Left, diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 34afcc078f..6c685fdd5c 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -24,18 +24,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), - SettingsPageItem::SettingItem(SettingItem { - title: "Restore On Startup", - description: "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, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "When Closing With No Tabs", description: "What to do when using the 'close active item' action with no tabs", @@ -100,6 +88,54 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Private Files", + description: "Globs to match against file paths to determine if a file is private", + field: Box::new( + SettingField { + pick: |settings_content| { + &settings_content.project.worktree.private_files + }, + pick_mut: |settings_content| { + &mut settings_content.project.worktree.private_files + }, + } + .unimplemented(), + ), + metadata: None, + files: USER, + }), + SettingsPageItem::SectionHeader("Workspace Restoration"), + SettingsPageItem::SettingItem(SettingItem { + title: "Restore Unsaved Buffers", + description: "Whether or not to restore unsaved buffers on restart", + field: Box::new(SettingField { + pick: |settings_content| match settings_content.session.as_ref() { + Some(session) => &session.restore_unsaved_buffers, + None => &None, + }, + pick_mut: |settings_content| { + &mut settings_content + .session + .get_or_insert_default() + .restore_unsaved_buffers + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Restore On Startup", + description: "What to restore from the 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, + files: USER, + }), SettingsPageItem::SectionHeader("Scoped Settings"), SettingsPageItem::SettingItem(SettingItem { // todo(settings_ui): Implement another setting item type that just shows an edit in settings.json @@ -172,10 +208,21 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Auto Update"), + 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, + files: USER, + }), ], }, SettingsPage { - title: "Appearance & Behavior", + title: "Appearance", items: vec![ SettingsPageItem::SectionHeader("Theme"), // todo(settings_ui): Figure out how we want to add these @@ -254,6 +301,36 @@ pub(crate) fn settings_data(cx: &App) -> Vec { ), metadata: None, }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "Buffer Font Features", + description: "The OpenType features to enable for rendering in text buffers.", + field: Box::new( + SettingField { + pick: |settings_content| &settings_content.theme.buffer_font_features, + pick_mut: |settings_content| { + &mut settings_content.theme.buffer_font_features + }, + } + .unimplemented(), + ), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "Buffer Font Fallbacks", + description: "The font fallbacks to use for rendering in text buffers.", + field: Box::new( + SettingField { + pick: |settings_content| &settings_content.theme.buffer_font_fallbacks, + pick_mut: |settings_content| { + &mut settings_content.theme.buffer_font_fallbacks + }, + } + .unimplemented(), + ), + metadata: None, + }), SettingsPageItem::SettingItem(SettingItem { title: "UI Font Family", description: "Font family for UI elements", @@ -284,6 +361,36 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "UI Font Features", + description: "The OpenType features to enable for rendering in UI elements.", + field: Box::new( + SettingField { + pick: |settings_content| &settings_content.theme.ui_font_features, + pick_mut: |settings_content| { + &mut settings_content.theme.ui_font_features + }, + } + .unimplemented(), + ), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "UI Font Fallbacks", + description: "The font fallbacks to use for rendering in the UI.", + field: Box::new( + SettingField { + pick: |settings_content| &settings_content.theme.ui_font_fallbacks, + pick_mut: |settings_content| { + &mut settings_content.theme.ui_font_fallbacks + }, + } + .unimplemented(), + ), + metadata: None, + }), SettingsPageItem::SettingItem(SettingItem { title: "Agent Panel UI Font Size", description: "Font size for agent response text in the agent panel. Falls back to the regular UI font size.", @@ -312,39 +419,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), - 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, - files: USER, - }), - // 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: "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, - files: USER, - }), - SettingsPageItem::SettingItem(SettingItem { - title: "Helix Mode", - description: "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, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "Multi Cursor Modifier", description: "Modifier key for adding multiple cursors", @@ -490,87 +564,44 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER | LOCAL, }), - SettingsPageItem::SectionHeader("Layout"), + ], + }, + SettingsPage { + title: "Keymap", + items: vec![ + SettingsPageItem::SectionHeader("Base Keymap"), SettingsPageItem::SettingItem(SettingItem { - title: "Bottom Dock Layout", - description: "Layout mode for the bottom dock", + title: "Base Keymap", + description: "The name of a base set of key bindings to use", field: Box::new(SettingField { - pick: |settings_content| &settings_content.workspace.bottom_dock_layout, - pick_mut: |settings_content| { - &mut settings_content.workspace.bottom_dock_layout - }, + pick: |settings_content| &settings_content.base_keymap, + pick_mut: |settings_content| &mut settings_content.base_keymap, + }), + metadata: Some(Box::new(SettingsFieldMetadata { + should_do_titlecase: Some(false), + ..Default::default() + })), + files: USER, + }), + SettingsPageItem::SectionHeader("Modal Editing"), + // 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: "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, files: USER, }), SettingsPageItem::SettingItem(SettingItem { - files: USER, - title: "Centered Layout Left Padding", - description: "Left padding for centered layout", + title: "Helix Mode", + description: "Enable helix modes and key bindings", field: Box::new(SettingField { - pick: |settings_content| { - if let Some(centered_layout) = - &settings_content.workspace.centered_layout - { - ¢ered_layout.left_padding - } else { - &None - } - }, - pick_mut: |settings_content| { - &mut settings_content - .workspace - .centered_layout - .get_or_insert_default() - .left_padding - }, - }), - metadata: None, - }), - SettingsPageItem::SettingItem(SettingItem { - files: USER, - title: "Centered Layout Right Padding", - description: "Right padding for centered layout", - field: Box::new(SettingField { - pick: |settings_content| { - if let Some(centered_layout) = - &settings_content.workspace.centered_layout - { - ¢ered_layout.right_padding - } else { - &None - } - }, - pick_mut: |settings_content| { - &mut settings_content - .workspace - .centered_layout - .get_or_insert_default() - .right_padding - }, - }), - metadata: None, - }), - SettingsPageItem::SettingItem(SettingItem { - title: "Zoomed Padding", - description: "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, - files: USER, - }), - SettingsPageItem::SectionHeader("Window"), - // todo(settings_ui): Should we filter by platform? - SettingsPageItem::SettingItem(SettingItem { - title: "Use System Window Tabs", - description: "(macOS only) Whether to allow windows to tab together", - 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 - }, + pick: |settings_content| &settings_content.helix_mode, + pick_mut: |settings_content| &mut settings_content.helix_mode, }), metadata: None, files: USER, @@ -581,6 +612,22 @@ pub(crate) fn settings_data(cx: &App) -> Vec { title: "Editor", items: { let mut items = vec![ + SettingsPageItem::SectionHeader("Auto Save"), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Save Mode", + description: "When to Auto Save Buffer Changes", + field: Box::new( + SettingField { + pick: |settings_content| &settings_content.workspace.autosave, + pick_mut: |settings_content| { + &mut settings_content.workspace.autosave + }, + } + .unimplemented(), + ), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Multibuffer"), SettingsPageItem::SettingItem(SettingItem { title: "Double Click In Multibuffer", @@ -620,6 +667,27 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Expand Outlines With Depth", + description: "Default depth to expand outline items in the current file", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(outline_panel) = &settings_content.outline_panel { + &outline_panel.expand_outlines_with_depth + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .outline_panel + .get_or_insert_default() + .expand_outlines_with_depth + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Scrolling"), SettingsPageItem::SettingItem(SettingItem { title: "Scroll Beyond Last Line", @@ -1409,6 +1477,242 @@ pub(crate) fn settings_data(cx: &App) -> Vec { items: { let mut items = vec![]; items.extend(non_editor_language_settings_data()); + items.extend([ + SettingsPageItem::SectionHeader("File Types"), + SettingsPageItem::SettingItem(SettingItem { + title: "File Type Associations", + description: "A Mapping from Languages to files and file extensions that should be treated as that language", + field: Box::new( + SettingField { + pick: |settings_content| { + &settings_content.project.all_languages.file_types + }, + pick_mut: |settings_content| { + &mut settings_content.project.all_languages.file_types + }, + } + .unimplemented(), + ), + metadata: None, + files: USER | LOCAL, + }), + ]); + + items.extend([ + SettingsPageItem::SectionHeader("Diagnostics"), + 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, + files: USER, + }), + 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, + files: USER, + }), + SettingsPageItem::SectionHeader("Inline Diagnostics"), + SettingsPageItem::SettingItem(SettingItem { + title: "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, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "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, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "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, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Minimum Column", + description: "The minimum column at which 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, + files: USER, + }), + SettingsPageItem::SectionHeader("LSP Pull Diagnostics"), + SettingsPageItem::SettingItem(SettingItem { + title: "Enabled", + description: "Whether to pull for language server-powered 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, + files: USER, + }), + // todo(settings_ui): Needs unit + SettingsPageItem::SettingItem(SettingItem { + title: "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, + files: USER, + }), + SettingsPageItem::SectionHeader("LSP Highlights"), + SettingsPageItem::SettingItem(SettingItem { + title: "Debounce", + description: "The debounce delay before querying highlights from the language", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.lsp_highlight_debounce, + pick_mut: |settings_content| { + &mut settings_content.editor.lsp_highlight_debounce + }, + }), + metadata: None, + files: USER, + }), + ]); + // todo(settings_ui): Refresh on extension (un)/installed // Note that `crates/json_schema_store` solves the same problem, there is probably a way to unify the two items.push(SettingsPageItem::SectionHeader(LANGUAGES_SECTION_HEADER)); @@ -1481,6 +1785,18 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Smartcase Search", + description: "Whether to automatically enable case-sensitive search based on the search query", + 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, + files: USER, + }), SettingsPageItem::SettingItem(SettingItem { title: "Include Ignored", description: "Include ignored files in search results by default", @@ -1521,6 +1837,31 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + 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, + files: USER, + }), + 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, + files: USER, + }), + SettingsPageItem::SectionHeader("File Finder"), // todo: null by default SettingsPageItem::SettingItem(SettingItem { title: "Include Ignored in Search", @@ -1546,43 +1887,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), - 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, - files: USER, - }), - 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, - files: USER, - }), - SettingsPageItem::SettingItem(SettingItem { - title: "Use Smartcase Search", - description: "Use smartcase (i.e., case-sensitive) 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, - files: USER, - }), - SettingsPageItem::SectionHeader("File Management"), SettingsPageItem::SettingItem(SettingItem { title: "File Icons", description: "Show file icons in the file finder", @@ -1625,30 +1929,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), - SettingsPageItem::SettingItem(SettingItem { - title: "Restore File State", - description: "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, - files: USER, - }), - SettingsPageItem::SettingItem(SettingItem { - title: "Close on File Delete", - description: "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, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "Skip Focus For Active In Search", description: "Whether the file finder should skip focus for the active file in search results", @@ -1692,10 +1972,68 @@ pub(crate) fn settings_data(cx: &App) -> Vec { files: USER, }), SettingsPageItem::SectionHeader("File Scan"), + SettingsPageItem::SettingItem(SettingItem { + title: "File Scan Exclusions", + description: "Files or globs of files that will be excluded by Zed entirely. They will be skipped during file scans, file searches, and not be displayed in the project file tree. Takes precedence over \"File Scan Inclusions\"", + field: Box::new( + SettingField { + pick: |settings_content| { + &settings_content.project.worktree.file_scan_exclusions + }, + pick_mut: |settings_content| { + &mut settings_content.project.worktree.file_scan_exclusions + }, + } + .unimplemented(), + ), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "File Scan Inclusions", + description: "Files or globs of files that will be included by Zed, even when ignored by git. This is useful for files that are not tracked by git, but are still important to your project. Note that globs that are overly broad can slow down Zed's file scanning. \"File Scan Exclusions\" takes precedence over these inclusions", + field: Box::new( + SettingField { + pick: |settings_content| { + &settings_content.project.worktree.file_scan_exclusions + }, + pick_mut: |settings_content| { + &mut settings_content.project.worktree.file_scan_exclusions + }, + } + .unimplemented(), + ), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Restore File State", + description: "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, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Close on File Delete", + description: "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, + files: USER, + }), ], }, SettingsPage { - title: "Workbench & Window", + title: "Window & Layout", items: vec![ SettingsPageItem::SectionHeader("Status Bar"), SettingsPageItem::SettingItem(SettingItem { @@ -2004,24 +2342,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), - SettingsPageItem::SettingItem(SettingItem { - title: "Editor Tabs", - description: "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, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "Show Git Status In Tabs", description: "Show the Git file status on a tab item", @@ -2240,6 +2560,167 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + 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, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "Centered Layout Left Padding", + description: "Left padding for centered layout", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(centered_layout) = + &settings_content.workspace.centered_layout + { + ¢ered_layout.left_padding + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .workspace + .centered_layout + .get_or_insert_default() + .left_padding + }, + }), + metadata: None, + }), + SettingsPageItem::SettingItem(SettingItem { + files: USER, + title: "Centered Layout Right Padding", + description: "Right padding for centered layout", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(centered_layout) = + &settings_content.workspace.centered_layout + { + ¢ered_layout.right_padding + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .workspace + .centered_layout + .get_or_insert_default() + .right_padding + }, + }), + metadata: None, + }), + SettingsPageItem::SectionHeader("Window"), + // todo(settings_ui): Should we filter by platform? + SettingsPageItem::SettingItem(SettingItem { + title: "Use System Window Tabs", + description: "(macOS only) Whether to allow windows to tab together", + 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, + files: USER, + }), + SettingsPageItem::SectionHeader("Pane Modifiers"), + SettingsPageItem::SettingItem(SettingItem { + title: "Inactive Opacity", + description: "Opacity of inactive panels (0.0 - 1.0)", + field: Box::new(SettingField { + pick: |settings_content| match settings_content + .workspace + .active_pane_modifiers + .as_ref() + { + Some(modifiers) => &modifiers.inactive_opacity, + None => &None, + }, + pick_mut: |settings_content| { + &mut settings_content + .workspace + .active_pane_modifiers + .get_or_insert_default() + .inactive_opacity + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Border Size", + description: "Size of the border surrounding the active pane", + field: Box::new(SettingField { + pick: |settings_content| match settings_content + .workspace + .active_pane_modifiers + .as_ref() + { + Some(modifiers) => &modifiers.border_size, + None => &None, + }, + pick_mut: |settings_content| { + &mut settings_content + .workspace + .active_pane_modifiers + .get_or_insert_default() + .border_size + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Zoomed Padding", + description: "Show padding for zoomed panes", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.workspace.zoomed_padding, + pick_mut: |settings_content| &mut settings_content.workspace.zoomed_padding, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SectionHeader("Pane Split Direction"), + SettingsPageItem::SettingItem(SettingItem { + title: "Vertical Split Direction", + description: "Direction to split vertically", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.workspace.pane_split_direction_vertical + }, + pick_mut: |settings_content| { + &mut settings_content.workspace.pane_split_direction_vertical + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Horizontal Split Direction", + description: "Direction to split horizontally", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.workspace.pane_split_direction_horizontal + }, + pick_mut: |settings_content| { + &mut settings_content.workspace.pane_split_direction_horizontal + }, + }), + metadata: None, + files: USER, + }), ], }, SettingsPage { @@ -2931,6 +3412,113 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Git Panel Status Style", + description: "How entry statuses are displayed", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.status_style + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .status_style + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Fallback Branch Name", + description: "Default branch name will be when init.defaultBranch is not set in git", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.fallback_branch_name + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .fallback_branch_name + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Sort By Path", + description: "Enable to sort entries in the panel by path, disable to sort by status", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.sort_by_path + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .sort_by_path + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Collapse Untracked Diff", + description: "Whether to collapse untracked files in the diff panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(git_panel) = &settings_content.git_panel { + &git_panel.collapse_untracked_diff + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .collapse_untracked_diff + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Scroll Bar", + description: "How and when the scrollbar should be displayed", + field: Box::new(SettingField { + pick: |settings_content| match &settings_content.git_panel { + Some(settings::GitPanelSettingsContent { + scrollbar: Some(scrollbar), + .. + }) => &scrollbar.show, + _ => &None, + }, + pick_mut: |settings_content| { + &mut settings_content + .git_panel + .get_or_insert_default() + .scrollbar + .get_or_insert_default() + .show + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Debugger Panel"), SettingsPageItem::SettingItem(SettingItem { title: "Debugger Panel Dock", @@ -3785,9 +4373,9 @@ pub(crate) fn settings_data(cx: &App) -> Vec { SettingsPage { title: "Version Control", items: vec![ - SettingsPageItem::SectionHeader("Git"), + SettingsPageItem::SectionHeader("Git Gutter"), SettingsPageItem::SettingItem(SettingItem { - title: "Git Gutter", + title: "Visibilility", description: "Control whether git status is shown in the editor's gutter", field: Box::new(SettingField { pick: |settings_content| { @@ -3806,7 +4394,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { }), // todo(settings_ui): Figure out the right default for this value in default.json SettingsPageItem::SettingItem(SettingItem { - title: "Gutter Debounce", + title: "Debounce", description: "Debounce threshold in milliseconds after which changes are reflected in the git gutter", field: Box::new(SettingField { pick: |settings_content| { @@ -3823,8 +4411,9 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Inline Git Blame"), SettingsPageItem::SettingItem(SettingItem { - title: "Inline Git Blame", + title: "Enabled", description: "Whether or not to show git blame data inline in the currently focused line", field: Box::new(SettingField { pick: |settings_content| { @@ -3851,7 +4440,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { files: USER, }), SettingsPageItem::SettingItem(SettingItem { - title: "Inline Git Blame Delay", + title: "Delay", description: "The delay after which the inline blame information is shown", field: Box::new(SettingField { pick: |settings_content| { @@ -3878,7 +4467,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { files: USER, }), SettingsPageItem::SettingItem(SettingItem { - title: "Inline Git Blame Padding", + title: "Padding", description: "Padding between the end of the source line and the start of the inline blame in columns", field: Box::new(SettingField { pick: |settings_content| { @@ -3905,19 +4494,15 @@ pub(crate) fn settings_data(cx: &App) -> Vec { files: USER, }), SettingsPageItem::SettingItem(SettingItem { - title: "Inline Git Blame Min Column", - description: "The minimum column number to show the inline blame information at", + title: "Minimum Column", + description: "The minimum column number at which to show the inline blame information", field: Box::new(SettingField { - pick: |settings_content| { - if let Some(git) = &settings_content.git { - if let Some(inline_blame) = &git.inline_blame { - &inline_blame.min_column - } else { - &None - } - } else { - &None - } + pick: |settings_content| match &settings_content.git { + Some(settings::GitSettings { + inline_blame: Some(inline_blame), + .. + }) => &inline_blame.min_column, + _ => &None, }, pick_mut: |settings_content| { &mut settings_content @@ -3958,6 +4543,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Git Blame View"), SettingsPageItem::SettingItem(SettingItem { title: "Show Avatar", description: "Show the avatar of the author of the commit", @@ -3985,8 +4571,9 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Branch Picker"), SettingsPageItem::SettingItem(SettingItem { - title: "Show Author Name In Branch Picker", + title: "Show Author Name", description: "Show author name as part of the commit information in branch picker", field: Box::new(SettingField { pick: |settings_content| { @@ -4012,6 +4599,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Git Hunks"), SettingsPageItem::SettingItem(SettingItem { title: "Hunk Style", description: "How git hunks are displayed visually in the editor", @@ -4032,210 +4620,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec { }), ], }, - SettingsPage { - title: "Diagnostics & Errors", - items: vec![ - 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, - files: USER, - }), - 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, - files: USER, - }), - 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, - files: USER, - }), - 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, - files: USER, - }), - 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, - files: USER, - }), - 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, - files: USER, - }), - SettingsPageItem::SectionHeader("Performance"), - SettingsPageItem::SettingItem(SettingItem { - title: "LSP Pull Diagnostics Enabled", - description: "Whether to pull for language server-powered 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, - files: USER, - }), - // todo(settings_ui): Needs unit - 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, - files: USER, - }), - ], - }, SettingsPage { title: "Collaboration", items: vec![ @@ -4585,7 +4969,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { ], }, SettingsPage { - title: "System & Network", + title: "Network", items: vec![ SettingsPageItem::SectionHeader("Network"), // todo(settings_ui): Proxy needs a default @@ -4601,6 +4985,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { ), metadata: Some(Box::new(SettingsFieldMetadata { placeholder: Some("socks5h://localhost:10808"), + ..Default::default() })), files: USER, }), @@ -4613,20 +4998,10 @@ pub(crate) fn settings_data(cx: &App) -> Vec { }), metadata: Some(Box::new(SettingsFieldMetadata { placeholder: Some("https://zed.dev"), + ..Default::default() })), files: USER, }), - 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, - files: USER, - }), ], }, ] @@ -4675,7 +5050,7 @@ fn language_settings_field_mut( } fn language_settings_data() -> Vec { - vec![ + let mut items = vec![ SettingsPageItem::SectionHeader("Indentation"), SettingsPageItem::SettingItem(SettingItem { title: "Tab Size", @@ -5525,6 +5900,20 @@ fn language_settings_data() -> Vec { metadata: None, files: USER | LOCAL, }), + ]; + if current_language().is_none() { + items.push(SettingsPageItem::SettingItem(SettingItem { + title: "LSP Document Colors", + description: "How to render LSP color previews in the editor", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.editor.lsp_document_colors, + pick_mut: |settings_content| &mut settings_content.editor.lsp_document_colors, + }), + metadata: None, + files: USER, + })) + } + items.extend([ SettingsPageItem::SectionHeader("Tasks"), SettingsPageItem::SettingItem(SettingItem { title: "Enabled", @@ -5643,7 +6032,63 @@ fn language_settings_data() -> Vec { metadata: None, files: USER | LOCAL, }), - ] + ]); + + if current_language().is_none() { + items.extend([ + SettingsPageItem::SettingItem(SettingItem { + title: "Image Viewer", + description: "The unit for image file sizes", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(image_viewer) = settings_content.image_viewer.as_ref() { + &image_viewer.unit + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.image_viewer.get_or_insert_default().unit + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Auto Replace Emoji Shortcode", + description: "Whether to automatically replace emoji shortcodes with emoji characters", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(message_editor) = settings_content.message_editor.as_ref() { + &message_editor.auto_replace_emoji_shortcode + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.message_editor.get_or_insert_default().auto_replace_emoji_shortcode + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Drop Size Target", + description: "Relative size of the drop target in the editor that will open dropped file as a split pane", + field: Box::new(SettingField { + pick: |settings_content| { + &settings_content.workspace.drop_target_size + }, + pick_mut: |settings_content| { + &mut settings_content.workspace.drop_target_size + }, + }), + metadata: None, + files: USER, + }), + ]); + } + items } /// LanguageSettings items that should be included in the "Languages & Tools" page diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 8867a5b41f..ea83187a66 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -1,4 +1,3 @@ -//! # settings_ui mod components; mod page_data; @@ -16,10 +15,7 @@ use heck::ToTitleCase as _; use project::WorktreeId; use schemars::JsonSchema; use serde::Deserialize; -use settings::{ - BottomDockLayout, CloseWindowWhenNoItems, CodeFade, CursorShape, OnLastWindowClosed, - RestoreOnStartupBehavior, SaturatingBool, SettingsContent, SettingsStore, -}; +use settings::{SettingsContent, SettingsStore}; use std::{ any::{Any, TypeId, type_name}, cell::RefCell, @@ -330,8 +326,10 @@ impl Focusable for NonFocusableHandle { } } +#[derive(Default)] struct SettingsFieldMetadata { placeholder: Option<&'static str>, + should_do_titlecase: Option, } pub struct SettingsUiFeatureFlag; @@ -369,12 +367,12 @@ fn init_renderers(cx: &mut App) { }) .add_basic_renderer::(render_toggle_button) .add_basic_renderer::(render_text_field) - .add_basic_renderer::(render_toggle_button) - .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_toggle_button) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) .add_basic_renderer::(render_font_picker) // todo(settings_ui): This needs custom ui // .add_renderer::(|settings_field, file, _, window, cx| { @@ -421,7 +419,7 @@ fn init_renderers(cx: &mut App) { .add_basic_renderer::(render_number_field) .add_basic_renderer::>(render_number_field) .add_basic_renderer::(render_number_field) - .add_basic_renderer::(render_number_field) + .add_basic_renderer::(render_number_field) .add_basic_renderer::(render_number_field) .add_basic_renderer::(render_number_field) .add_basic_renderer::(render_dropdown) @@ -431,7 +429,15 @@ fn init_renderers(cx: &mut App) { .add_basic_renderer::(render_dropdown) .add_basic_renderer::(render_dropdown) .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown); + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown) + // please semicolon stay on next line + ; // .add_renderer::(|settings_field, file, _, window, cx| { // render_dropdown(*settings_field, file, window, cx) // }); @@ -1913,7 +1919,7 @@ impl SettingsWindow { return; }; self.page_scroll_handle - .scroll_to_top_of_item(selected_item_index); + .scroll_to_top_of_item(selected_item_index + 1); if focus_content { self.focus_content_element(entry_item_index, window, cx); @@ -2606,7 +2612,7 @@ fn render_number_field( fn render_dropdown( field: SettingField, file: SettingsUiFile, - _metadata: Option<&SettingsFieldMetadata>, + metadata: Option<&SettingsFieldMetadata>, window: &mut Window, cx: &mut App, ) -> AnyElement @@ -2615,6 +2621,9 @@ where { let variants = || -> &'static [T] { ::VARIANTS }; let labels = || -> &'static [&'static str] { ::VARIANTS }; + let should_do_titlecase = metadata + .and_then(|metadata| metadata.should_do_titlecase) + .unwrap_or(true); let (_, current_value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick); @@ -2625,12 +2634,20 @@ where DropdownMenu::new( "dropdown", - current_value_label.to_title_case(), + if should_do_titlecase { + current_value_label.to_title_case() + } else { + current_value_label.to_string() + }, ContextMenu::build(window, cx, move |mut menu, _, _| { for (&value, &label) in std::iter::zip(variants(), labels()) { let file = file.clone(); menu = menu.toggleable_entry( - label.to_title_case(), + if should_do_titlecase { + label.to_title_case() + } else { + label.to_string() + }, value == current_value, IconPosition::End, None,