settings_ui: Add more settings (#40708)

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle
2025-10-20 12:37:55 -04:00
committed by GitHub
parent 0a17f91923
commit b6fb1d0a19
11 changed files with 650 additions and 81 deletions
+4 -4
View File
@@ -1091,10 +1091,10 @@
// Only the file Zed had indexed will be used, not necessary all the gitignored files.
//
// Can accept 3 values:
// * `true`: Use all gitignored files
// * `false`: Use only the files Zed had indexed
// * `null`: Be smart and search for ignored when called from a gitignored worktree
"include_ignored": null
// * "all": Use all gitignored files
// * "indexed": Use only the files Zed had indexed
// * "smart": Be smart and search for ignored when called from a gitignored worktree
"include_ignored": "smart"
},
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
@@ -18,7 +18,11 @@ impl Settings for FileFinderSettings {
file_icons: file_finder.file_icons.unwrap(),
modal_max_width: file_finder.modal_max_width.unwrap().into(),
skip_focus_for_active_in_search: file_finder.skip_focus_for_active_in_search.unwrap(),
include_ignored: file_finder.include_ignored,
include_ignored: match file_finder.include_ignored.unwrap() {
settings::IncludeIgnoredContent::All => Some(true),
settings::IncludeIgnoredContent::Indexed => Some(false),
settings::IncludeIgnoredContent::Smart => None,
},
}
}
}
+6
View File
@@ -123,3 +123,9 @@ pub(crate) mod m_2025_10_16 {
pub(crate) use settings::restore_code_actions_on_format;
}
pub(crate) mod m_2025_10_17 {
mod settings;
pub(crate) use settings::make_file_finder_include_ignored_an_enum;
}
@@ -0,0 +1,23 @@
use anyhow::Result;
use serde_json::Value;
pub fn make_file_finder_include_ignored_an_enum(value: &mut Value) -> Result<()> {
let Some(file_finder) = value.get_mut("file_finder") else {
return Ok(());
};
let Some(file_finder_obj) = file_finder.as_object_mut() else {
anyhow::bail!("Expected file_finder to be an object");
};
let Some(include_ignored) = file_finder_obj.get_mut("include_ignored") else {
return Ok(());
};
*include_ignored = match include_ignored {
Value::Bool(true) => Value::String("all".to_string()),
Value::Bool(false) => Value::String("indexed".to_string()),
Value::Null => Value::String("smart".to_string()),
_ => anyhow::bail!("Expected include_ignored to be a boolean or null"),
};
Ok(())
}
+72
View File
@@ -212,6 +212,7 @@ pub fn migrate_settings(text: &str) -> Result<Option<String>> {
&SETTINGS_QUERY_2025_10_03,
),
MigrationType::Json(migrations::m_2025_10_16::restore_code_actions_on_format),
MigrationType::Json(migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum),
];
run_migrations(text, migrations)
}
@@ -2090,4 +2091,75 @@ mod tests {
),
);
}
#[test]
fn test_make_file_finder_include_ignored_an_enum() {
assert_migrate_settings_with_migrations(
&[MigrationType::Json(
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
)],
&r#"{ }"#.unindent(),
None,
);
assert_migrate_settings_with_migrations(
&[MigrationType::Json(
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
)],
&r#"{
"file_finder": {
"include_ignored": true
}
}"#
.unindent(),
Some(
&r#"{
"file_finder": {
"include_ignored": "all"
}
}"#
.unindent(),
),
);
assert_migrate_settings_with_migrations(
&[MigrationType::Json(
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
)],
&r#"{
"file_finder": {
"include_ignored": false
}
}"#
.unindent(),
Some(
&r#"{
"file_finder": {
"include_ignored": "indexed"
}
}"#
.unindent(),
),
);
assert_migrate_settings_with_migrations(
&[MigrationType::Json(
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
)],
&r#"{
"file_finder": {
"include_ignored": null
}
}"#
.unindent(),
Some(
&r#"{
"file_finder": {
"include_ignored": "smart"
}
}"#
.unindent(),
),
);
}
}
+27 -8
View File
@@ -608,14 +608,33 @@ pub struct FileFinderSettingsContent {
/// Whether to use gitignored files when searching.
/// Only the file Zed had indexed will be used, not necessary all the gitignored files.
///
/// Can accept 3 values:
/// * `Some(true)`: Use all gitignored files
/// * `Some(false)`: Use only the files Zed had indexed
/// * `None`: Be smart and search for ignored when called from a gitignored worktree
///
/// Default: None
/// todo() -> Change this type to an enum
pub include_ignored: Option<bool>,
/// Default: Smart
pub include_ignored: Option<IncludeIgnoredContent>,
}
#[derive(
Debug,
PartialEq,
Eq,
Clone,
Copy,
Default,
Serialize,
Deserialize,
JsonSchema,
MergeFrom,
strum::VariantArray,
strum::VariantNames,
)]
#[serde(rename_all = "snake_case")]
pub enum IncludeIgnoredContent {
/// Use all gitignored files
All,
/// Use only the files Zed had indexed
Indexed,
/// Be smart and search for ignored when called from a gitignored worktree
#[default]
Smart,
}
#[derive(
@@ -153,7 +153,18 @@ pub enum Shell {
},
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
#[derive(
Clone,
Debug,
Serialize,
Deserialize,
PartialEq,
Eq,
JsonSchema,
MergeFrom,
strum::EnumDiscriminants,
)]
#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
#[serde(rename_all = "snake_case")]
pub enum WorkingDirectory {
/// Use the current file's project directory. Will Fallback to the
+25 -2
View File
@@ -160,7 +160,18 @@ pub enum ThemeSelection {
}
/// Represents the selection of an icon theme, which can be either static or dynamic.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
#[derive(
Clone,
Debug,
Serialize,
Deserialize,
JsonSchema,
MergeFrom,
PartialEq,
Eq,
strum::EnumDiscriminants,
)]
#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
#[serde(untagged)]
pub enum IconThemeSelection {
/// A static icon theme selection, represented by a single icon theme name.
@@ -290,7 +301,19 @@ impl From<FontFamilyName> for String {
}
/// The buffer's line height.
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
#[derive(
Clone,
Copy,
Debug,
Serialize,
Deserialize,
PartialEq,
JsonSchema,
MergeFrom,
Default,
strum::EnumDiscriminants,
)]
#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
#[serde(rename_all = "snake_case")]
pub enum BufferLineHeight {
/// A less dense line height.
@@ -382,7 +382,19 @@ pub struct StatusBarSettingsContent {
pub cursor_position_button: Option<bool>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
#[derive(
Copy,
Clone,
Debug,
Serialize,
Deserialize,
PartialEq,
Eq,
JsonSchema,
MergeFrom,
strum::EnumDiscriminants,
)]
#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
#[serde(rename_all = "snake_case")]
pub enum AutosaveSetting {
/// Disable autosave.
+399 -64
View File
@@ -252,7 +252,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
description: "How to select the theme",
field: Box::new(SettingField {
pick: |settings_content| {
Some(&<<settings::ThemeSelection as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS[
Some(&dynamic_variants::<settings::ThemeSelection>()[
settings_content
.theme
.theme
@@ -263,7 +263,9 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
let Some(value) = value else {
return;
};
let settings_value = settings_content.theme.theme.as_mut().expect("Has Default");
let settings_value = settings_content.theme.theme.get_or_insert_with(|| {
settings::ThemeSelection::Static(theme::ThemeName(theme::default_theme(theme::SystemAppearance::default().0).into()))
});
*settings_value = match value {
settings::ThemeSelectionDiscriminants::Static => {
let name = match settings_value {
@@ -298,7 +300,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
pick_discriminant: |settings_content| {
Some(settings_content.theme.theme.as_ref()?.discriminant() as usize)
},
fields: <<settings::ThemeSelection as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS.into_iter().map(|variant| {
fields: dynamic_variants::<settings::ThemeSelection>().into_iter().map(|variant| {
match variant {
settings::ThemeSelectionDiscriminants::Static => vec![
SettingItem {
@@ -407,20 +409,169 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
}
}).collect(),
}),
SettingsPageItem::SettingItem(SettingItem {
files: USER,
title: "Icon Theme",
// todo(settings_ui)
// This description is misleading because the icon theme is used in more places than the file explorer)
description: "Choose the icon theme for file explorer",
field: Box::new(
SettingField {
pick: |settings_content| settings_content.theme.icon_theme.as_ref(),
write: |settings_content, value|{ settings_content.theme.icon_theme = value;},
SettingsPageItem::DynamicItem(DynamicItem {
discriminant: SettingItem {
files: USER,
title: "Icon Theme",
description: "The Icon Theme Zed Will Associate With Files And Directories",
field: Box::new(SettingField {
pick: |settings_content| {
Some(&dynamic_variants::<settings::IconThemeSelection>()[
settings_content
.theme
.icon_theme
.as_ref()?
.discriminant() as usize])
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
let settings_value = settings_content.theme.icon_theme.get_or_insert_with(|| {
settings::IconThemeSelection::Static(settings::IconThemeName(theme::default_icon_theme().name.clone().into()))
});
*settings_value = match value {
settings::IconThemeSelectionDiscriminants::Static => {
let name = match settings_value {
settings::IconThemeSelection::Static(_) => return,
settings::IconThemeSelection::Dynamic { mode, light, dark } => {
match mode {
theme::ThemeMode::Light => light.clone(),
theme::ThemeMode::Dark => dark.clone(),
theme::ThemeMode::System => dark.clone(), // no cx, can't determine correct choice
}
},
};
settings::IconThemeSelection::Static(name)
},
settings::IconThemeSelectionDiscriminants::Dynamic => {
let static_name = match settings_value {
settings::IconThemeSelection::Static(theme_name) => theme_name.clone(),
settings::IconThemeSelection::Dynamic {..} => return,
};
settings::IconThemeSelection::Dynamic {
mode: settings::ThemeMode::System,
light: static_name.clone(),
dark: static_name,
}
},
};
},
}),
metadata: None,
},
pick_discriminant: |settings_content| {
Some(settings_content.theme.icon_theme.as_ref()?.discriminant() as usize)
},
fields: dynamic_variants::<settings::IconThemeSelection>().into_iter().map(|variant| {
match variant {
settings::IconThemeSelectionDiscriminants::Static => vec![
SettingItem {
files: USER,
title: "Icon Theme Name",
description: "The Name Of The Icon Theme To Use",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.theme.icon_theme.as_ref() {
Some(settings::IconThemeSelection::Static(name)) => Some(name),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.theme
.icon_theme.as_mut() {
Some(settings::IconThemeSelection::Static(theme_name)) => *theme_name = value,
_ => return
}
},
}),
metadata: None,
}
],
settings::IconThemeSelectionDiscriminants::Dynamic => vec![
SettingItem {
files: USER,
title: "Mode",
description: "How To Determine Whether to Use a Light or Dark Icon Theme",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.theme.icon_theme.as_ref() {
Some(settings::IconThemeSelection::Dynamic { mode, ..}) => Some(mode),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.theme
.icon_theme.as_mut() {
Some(settings::IconThemeSelection::Dynamic{ mode, ..}) => *mode = value,
_ => return
}
},
}),
metadata: None,
},
SettingItem {
files: USER,
title: "Light Icon Theme",
description: "The Icon Theme To Use When Mode Is Set To Light, Or When Mode Is Set To System And The System Is In Light Mode",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.theme.icon_theme.as_ref() {
Some(settings::IconThemeSelection::Dynamic { light, ..}) => Some(light),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.theme
.icon_theme.as_mut() {
Some(settings::IconThemeSelection::Dynamic{ light, ..}) => *light = value,
_ => return
}
},
}),
metadata: None,
},
SettingItem {
files: USER,
title: "Dark Icon Theme",
description: "The Icon Theme To Use When Mode Is Set To Dark, Or When Mode Is Set To System And The System Is In Dark Mode",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.theme.icon_theme.as_ref() {
Some(settings::IconThemeSelection::Dynamic { dark, ..}) => Some(dark),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.theme
.icon_theme.as_mut() {
Some(settings::IconThemeSelection::Dynamic{ dark, ..}) => *dark = value,
_ => return
}
},
}),
metadata: None,
}
],
}
.unimplemented(),
),
metadata: None,
}).collect(),
}),
SettingsPageItem::SectionHeader("Buffer Font"),
SettingsPageItem::SettingItem(SettingItem {
@@ -453,24 +604,79 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
metadata: None,
files: USER,
}),
// todo(settings_ui): This needs custom ui
SettingsPageItem::SettingItem(SettingItem {
files: USER,
title: "Line Height",
description: "Line height for editor text",
field: Box::new(
SettingField {
SettingsPageItem::DynamicItem(DynamicItem {
discriminant: SettingItem {
files: USER,
title: "Line Height",
description: "Line height for editor text",
field: Box::new(SettingField {
pick: |settings_content| {
settings_content.theme.buffer_line_height.as_ref()
Some(&dynamic_variants::<settings::BufferLineHeight>()[
settings_content
.theme
.buffer_line_height
.as_ref()?
.discriminant() as usize])
},
write: |settings_content, value| {
settings_content.theme.buffer_line_height = value;
let Some(value) = value else {
return;
};
let settings_value = settings_content.theme.buffer_line_height.get_or_insert_with(|| {
settings::BufferLineHeight::default()
});
*settings_value = match value {
settings::BufferLineHeightDiscriminants::Comfortable => {
settings::BufferLineHeight::Comfortable
},
settings::BufferLineHeightDiscriminants::Standard => {
settings::BufferLineHeight::Standard
},
settings::BufferLineHeightDiscriminants::Custom => {
let custom_value = theme::BufferLineHeight::from(*settings_value).value();
settings::BufferLineHeight::Custom(custom_value)
},
};
},
}),
metadata: None,
},
pick_discriminant: |settings_content| {
Some(settings_content.theme.buffer_line_height.as_ref()?.discriminant() as usize)
},
fields: dynamic_variants::<settings::BufferLineHeight>().into_iter().map(|variant| {
match variant {
settings::BufferLineHeightDiscriminants::Comfortable => vec![],
settings::BufferLineHeightDiscriminants::Standard => vec![],
settings::BufferLineHeightDiscriminants::Custom => vec![
SettingItem {
files: USER,
title: "Custom Line Height",
description: "Custom line height value (must be at least 1.0)",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.theme.buffer_line_height.as_ref() {
Some(settings::BufferLineHeight::Custom(value)) => Some(value),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.theme
.buffer_line_height.as_mut() {
Some(settings::BufferLineHeight::Custom(line_height)) => *line_height = f32::max(value, 1.0),
_ => return
}
},
}),
metadata: None,
}
],
}
.unimplemented(),
),
metadata: None,
}).collect(),
}),
SettingsPageItem::SettingItem(SettingItem {
files: USER,
@@ -835,22 +1041,86 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
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 {
SettingsPageItem::DynamicItem(DynamicItem {
discriminant: SettingItem {
files: USER,
title: "Auto Save Mode",
description: "When to Auto Save Buffer Changes",
field: Box::new(SettingField {
pick: |settings_content| {
settings_content.workspace.autosave.as_ref()
Some(&dynamic_variants::<settings::AutosaveSetting>()[
settings_content
.workspace
.autosave
.as_ref()?
.discriminant() as usize])
},
write: |settings_content, value| {
settings_content.workspace.autosave = value;
let Some(value) = value else {
return;
};
let settings_value = settings_content.workspace.autosave.get_or_insert_with(|| {
settings::AutosaveSetting::Off
});
*settings_value = match value {
settings::AutosaveSettingDiscriminants::Off => {
settings::AutosaveSetting::Off
},
settings::AutosaveSettingDiscriminants::AfterDelay => {
let milliseconds = match settings_value {
settings::AutosaveSetting::AfterDelay { milliseconds } => *milliseconds,
_ => settings::DelayMs(1000),
};
settings::AutosaveSetting::AfterDelay { milliseconds }
},
settings::AutosaveSettingDiscriminants::OnFocusChange => {
settings::AutosaveSetting::OnFocusChange
},
settings::AutosaveSettingDiscriminants::OnWindowChange => {
settings::AutosaveSetting::OnWindowChange
},
};
},
}),
metadata: None,
},
pick_discriminant: |settings_content| {
Some(settings_content.workspace.autosave.as_ref()?.discriminant() as usize)
},
fields: dynamic_variants::<settings::AutosaveSetting>().into_iter().map(|variant| {
match variant {
settings::AutosaveSettingDiscriminants::Off => vec![],
settings::AutosaveSettingDiscriminants::AfterDelay => vec![
SettingItem {
files: USER,
title: "Delay (milliseconds)",
description: "Save after inactivity period (in milliseconds)",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.workspace.autosave.as_ref() {
Some(settings::AutosaveSetting::AfterDelay { milliseconds }) => Some(milliseconds),
_ => None
}
},
write: |settings_content, value| {
let Some(value) = value else {
return;
};
match settings_content
.workspace
.autosave.as_mut() {
Some(settings::AutosaveSetting::AfterDelay { milliseconds }) => *milliseconds = value,
_ => return
}
},
}),
metadata: None,
}
],
settings::AutosaveSettingDiscriminants::OnFocusChange => vec![],
settings::AutosaveSettingDiscriminants::OnWindowChange => vec![],
}
.unimplemented(),
),
metadata: None,
files: USER,
}).collect(),
}),
SettingsPageItem::SectionHeader("Multibuffer"),
SettingsPageItem::SettingItem(SettingItem {
@@ -2088,7 +2358,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
.include_ignored = value;
},
}
.unimplemented(),
),
metadata: None,
files: USER,
@@ -3174,8 +3443,8 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
}),
SettingsPageItem::SettingItem(SettingItem {
files: USER,
title: "Indent Guides Show",
description: "Show indent guides in the project panel",
title: "Show Indent Guides",
description: "Show Indent Guides In The Project Panel",
field: Box::new(
SettingField {
pick: |settings_content| {
@@ -3196,7 +3465,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
.show = value;
},
}
.unimplemented(),
),
metadata: None,
}),
@@ -3466,8 +3734,8 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
}),
SettingsPageItem::SettingItem(SettingItem {
files: USER,
title: "Indent Guides Show",
description: "When to show indent guides in the outline panel",
title: "Show Indent Guides",
description: "When To Show Indent Guides In The Outline Panel",
field: Box::new(
SettingField {
pick: |settings_content| {
@@ -3488,7 +3756,6 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
.show = value;
},
}
.unimplemented(),
),
metadata: None,
}),
@@ -3959,31 +4226,91 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
metadata: None,
files: USER | LOCAL,
}),
SettingsPageItem::SettingItem(SettingItem {
title: "Working Directory",
description: "What working directory to use when launching the terminal",
field: Box::new(
SettingField {
SettingsPageItem::DynamicItem(DynamicItem {
discriminant: SettingItem {
files: USER | LOCAL,
title: "Working Directory",
description: "What working directory to use when launching the terminal",
field: Box::new(SettingField {
pick: |settings_content| {
settings_content
.terminal
.as_ref()?
.project
.working_directory
.as_ref()
Some(&dynamic_variants::<settings::WorkingDirectory>()[
settings_content
.terminal
.as_ref()?
.project
.working_directory
.as_ref()?
.discriminant() as usize])
},
write: |settings_content, value| {
settings_content
let Some(value) = value else {
return;
};
let settings_value = settings_content
.terminal
.get_or_insert_default()
.project
.working_directory = value;
.working_directory
.get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
*settings_value = match value {
settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
settings::WorkingDirectory::CurrentProjectDirectory
},
settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
settings::WorkingDirectory::FirstProjectDirectory
},
settings::WorkingDirectoryDiscriminants::AlwaysHome => {
settings::WorkingDirectory::AlwaysHome
},
settings::WorkingDirectoryDiscriminants::Always => {
let directory = match settings_value {
settings::WorkingDirectory::Always { .. } => return,
_ => String::new(),
};
settings::WorkingDirectory::Always { directory }
},
};
},
}),
metadata: None,
},
pick_discriminant: |settings_content| {
Some(settings_content.terminal.as_ref()?.project.working_directory.as_ref()?.discriminant() as usize)
},
fields: dynamic_variants::<settings::WorkingDirectory>().into_iter().map(|variant| {
match variant {
settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
settings::WorkingDirectoryDiscriminants::Always => vec![
SettingItem {
files: USER | LOCAL,
title: "Directory",
description: "The directory path to use (will be shell expanded)",
field: Box::new(SettingField {
pick: |settings_content| {
match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
_ => None
}
},
write: |settings_content, value| {
let value = value.unwrap_or_default();
match settings_content
.terminal
.get_or_insert_default()
.project
.working_directory.as_mut() {
Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
_ => return
}
},
}),
metadata: None,
}
],
}
.unimplemented(),
),
metadata: None,
files: USER | LOCAL,
}).collect(),
}),
SettingsPageItem::SettingItem(SettingItem {
title: "Environment Variables",
@@ -6293,3 +6620,11 @@ fn show_scrollbar_or_editor(
.as_ref()
.and_then(|scrollbar| scrollbar.show.as_ref()))
}
fn dynamic_variants<T>() -> &'static [T::Discriminant]
where
T: strum::IntoDiscriminant,
T::Discriminant: strum::VariantArray,
{
<<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
}
+64
View File
@@ -439,6 +439,13 @@ fn init_renderers(cx: &mut App) {
.add_basic_renderer::<settings::ThemeSelectionDiscriminants>(render_dropdown)
.add_basic_renderer::<settings::ThemeMode>(render_dropdown)
.add_basic_renderer::<settings::ThemeName>(render_theme_picker)
.add_basic_renderer::<settings::IconThemeSelectionDiscriminants>(render_dropdown)
.add_basic_renderer::<settings::IconThemeName>(render_icon_theme_picker)
.add_basic_renderer::<settings::BufferLineHeightDiscriminants>(render_dropdown)
.add_basic_renderer::<settings::AutosaveSettingDiscriminants>(render_dropdown)
.add_basic_renderer::<settings::WorkingDirectoryDiscriminants>(render_dropdown)
.add_basic_renderer::<settings::IncludeIgnoredContent>(render_dropdown)
.add_basic_renderer::<settings::ShowIndentGuides>(render_dropdown)
// please semicolon stay on next line
;
}
@@ -2942,6 +2949,63 @@ fn render_theme_picker(
.into_any_element()
}
fn render_icon_theme_picker(
field: SettingField<settings::IconThemeName>,
file: SettingsUiFile,
_metadata: Option<&SettingsFieldMetadata>,
window: &mut Window,
cx: &mut App,
) -> AnyElement {
let (_, value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick);
let current_value = value
.cloned()
.map(|icon_theme_name| icon_theme_name.0.into())
.unwrap_or_else(|| theme::default_icon_theme().name.clone());
DropdownMenu::new(
"font-picker",
current_value.clone(),
ContextMenu::build(window, cx, move |mut menu, _, cx| {
let all_theme_names = theme::ThemeRegistry::global(cx)
.list_icon_themes()
.into_iter()
.map(|theme| theme.name);
for theme_name in all_theme_names {
let file = file.clone();
let selected = theme_name.as_ref() == current_value.as_ref();
menu = menu.toggleable_entry(
theme_name.clone(),
selected,
IconPosition::End,
None,
move |_, cx| {
if selected {
return;
}
let theme_name = theme_name.clone();
update_settings_file(file.clone(), cx, move |settings, _cx| {
(field.write)(
settings,
Some(settings::IconThemeName(theme_name.into())),
);
})
.log_err(); // todo(settings_ui) don't log err
},
);
}
menu
}),
)
.trigger_size(ButtonSize::Medium)
.style(DropdownStyle::Outlined)
.offset(gpui::Point {
x: px(0.0),
y: px(2.0),
})
.tab_index(0)
.into_any_element()
}
#[cfg(test)]
mod test {