From e0ac8f7a9dafdf212840b11fe9e00b4f28c8044f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 17 Sep 2025 13:32:09 -0600 Subject: [PATCH] project panel --- crates/project_panel/src/project_panel.rs | 45 ++-- .../src/project_panel_settings.rs | 252 ++++++++---------- .../project_panel/src/project_panel_tests.rs | 81 +++--- crates/settings/src/settings_content.rs | 6 +- .../src/settings_content/workspace.rs | 92 ++++++- 5 files changed, 270 insertions(+), 206 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index b53dfe6fdf8bdf81761299c075eee302a06a8956..90c02be50d7cbe723c1ea16a8fd19bef21238ba6 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -36,12 +36,13 @@ use project::{ project_settings::GoToDiagnosticSeverityFilter, relativize_path, }; -use project_panel_settings::{ - ProjectPanelDockPosition, ProjectPanelSettings, ShowDiagnostics, ShowIndentGuides, -}; +use project_panel_settings::ProjectPanelSettings; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore, update_settings_file}; +use settings::{ + DockSide, ProjectPanelEntrySpacing, Settings, SettingsStore, ShowDiagnostics, ShowIndentGuides, + update_settings_file, +}; use smallvec::SmallVec; use std::{any::TypeId, time::Instant}; use std::{ @@ -343,8 +344,14 @@ pub fn init(cx: &mut App) { workspace.register_action(|workspace, _: &ToggleHideGitIgnore, _, cx| { let fs = workspace.app_state().fs.clone(); - update_settings_file::(fs, cx, move |setting, _| { - setting.hide_gitignore = Some(!setting.hide_gitignore.unwrap_or(false)); + update_settings_file(fs, cx, move |setting, _| { + setting.project_panel.get_or_insert_default().hide_gitignore = Some( + !setting + .project_panel + .get_or_insert_default() + .hide_gitignore + .unwrap_or(false), + ); }) }); @@ -4457,8 +4464,8 @@ impl ProjectPanel { .indent_level(depth) .indent_step_size(px(settings.indent_size)) .spacing(match settings.entry_spacing { - project_panel_settings::EntrySpacing::Comfortable => ListItemSpacing::Dense, - project_panel_settings::EntrySpacing::Standard => { + ProjectPanelEntrySpacing::Comfortable => ListItemSpacing::Dense, + ProjectPanelEntrySpacing::Standard => { ListItemSpacing::ExtraDense } }) @@ -5733,8 +5740,8 @@ impl EventEmitter for ProjectPanel {} impl Panel for ProjectPanel { fn position(&self, _: &Window, cx: &App) -> DockPosition { match ProjectPanelSettings::get_global(cx).dock { - ProjectPanelDockPosition::Left => DockPosition::Left, - ProjectPanelDockPosition::Right => DockPosition::Right, + DockSide::Left => DockPosition::Left, + DockSide::Right => DockPosition::Right, } } @@ -5743,17 +5750,13 @@ impl Panel for ProjectPanel { } fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context) { - settings::update_settings_file::( - self.fs.clone(), - cx, - move |settings, _| { - let dock = match position { - DockPosition::Left | DockPosition::Bottom => ProjectPanelDockPosition::Left, - DockPosition::Right => ProjectPanelDockPosition::Right, - }; - settings.dock = Some(dock); - }, - ); + settings::update_settings_file(self.fs.clone(), cx, move |settings, _| { + let dock = match position { + DockPosition::Left | DockPosition::Bottom => DockSide::Left, + DockPosition::Right => DockSide::Right, + }; + settings.project_panel.get_or_insert_default().dock = Some(dock); + }); } fn size(&self, _: &Window, cx: &App) -> Pixels { diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index 2a01d10a02449d47cad8a1c89bb9269f25db2725..b2646477697118e14abbbe6647a284d5a19a8866 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -2,40 +2,23 @@ use editor::EditorSettings; use gpui::Pixels; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; -use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar}; - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum ProjectPanelDockPosition { - Left, - Right, -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ShowIndentGuides { - Always, - Never, -} - -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum EntrySpacing { - /// Comfortable spacing of entries. - #[default] - Comfortable, - /// The standard spacing of entries. - Standard, -} +use settings::{ + DockSide, ProjectPanelEntrySpacing, Settings, SettingsContent, ShowDiagnostics, + ShowIndentGuides, +}; +use ui::{ + px, + scrollbars::{ScrollbarVisibility, ShowScrollbar}, +}; +use util::MergeFrom; #[derive(Deserialize, Debug, Clone, Copy, PartialEq)] pub struct ProjectPanelSettings { pub button: bool, pub hide_gitignore: bool, pub default_width: Pixels, - pub dock: ProjectPanelDockPosition, - pub entry_spacing: EntrySpacing, + pub dock: DockSide, + pub entry_spacing: ProjectPanelEntrySpacing, pub file_icons: bool, pub folder_icons: bool, pub git_status: bool, @@ -56,12 +39,6 @@ pub struct IndentGuidesSettings { pub show: ShowIndentGuides, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -pub struct IndentGuidesSettingsContent { - /// When to show the scrollbar in the project panel. - pub show: Option, -} - #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct ScrollbarSettings { /// When to show the scrollbar in the project panel. @@ -70,105 +47,6 @@ pub struct ScrollbarSettings { pub show: Option, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -pub struct ScrollbarSettingsContent { - /// When to show the scrollbar in the project panel. - /// - /// Default: inherits editor scrollbar settings - pub show: Option>, -} - -/// Whether to indicate diagnostic errors and/or warnings in project panel items. -/// -/// Default: all -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ShowDiagnostics { - /// Never mark the diagnostic errors/warnings in the project panel. - Off, - /// Mark files containing only diagnostic errors in the project panel. - Errors, - #[default] - /// Mark files containing diagnostic errors or warnings in the project panel. - All, -} - -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] -#[settings_key(key = "project_panel")] -pub struct ProjectPanelSettingsContent { - /// Whether to show the project panel button in the status bar. - /// - /// Default: true - pub button: Option, - /// Whether to hide gitignore files in the project panel. - /// - /// Default: false - pub hide_gitignore: Option, - /// Customize default width (in pixels) taken by project panel - /// - /// Default: 240 - pub default_width: Option, - /// The position of project panel - /// - /// Default: left - pub dock: Option, - /// Spacing between worktree entries in the project panel. - /// - /// Default: comfortable - pub entry_spacing: Option, - /// Whether to show file icons in the project panel. - /// - /// Default: true - pub file_icons: Option, - /// Whether to show folder icons or chevrons for directories in the project panel. - /// - /// Default: true - pub folder_icons: Option, - /// Whether to show the git status in the project panel. - /// - /// Default: true - pub git_status: Option, - /// Amount of indentation (in pixels) for nested items. - /// - /// Default: 20 - pub indent_size: Option, - /// Whether to reveal it in the project panel automatically, - /// when a corresponding project entry becomes active. - /// Gitignored entries are never auto revealed. - /// - /// Default: true - pub auto_reveal_entries: Option, - /// Whether to fold directories automatically - /// when directory has only one directory inside. - /// - /// Default: true - pub auto_fold_dirs: Option, - /// Whether the project panel should open on startup. - /// - /// Default: true - pub starts_open: Option, - /// Scrollbar-related settings - pub scrollbar: Option, - /// Which files containing diagnostic errors/warnings to mark in the project panel. - /// - /// Default: all - pub show_diagnostics: Option, - /// Settings related to indent guides in the project panel. - pub indent_guides: Option, - /// Whether to hide the root entry when only one folder is open in the window. - /// - /// Default: false - pub hide_root: Option, - /// Whether to stick parent directories at top of the project panel. - /// - /// Default: true - pub sticky_scroll: Option, - /// Whether to enable drag-and-drop operations in the project panel. - /// - /// Default: true - pub drag_and_drop: Option, -} - impl ScrollbarVisibility for ProjectPanelSettings { fn visibility(&self, cx: &ui::App) -> ShowScrollbar { self.scrollbar @@ -178,32 +56,112 @@ impl ScrollbarVisibility for ProjectPanelSettings { } impl Settings for ProjectPanelSettings { - type FileContent = ProjectPanelSettingsContent; + fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self { + let project_panel = content.project_panel.clone().unwrap(); + Self { + button: project_panel.button.unwrap(), + hide_gitignore: project_panel.hide_gitignore.unwrap(), + default_width: px(project_panel.default_width.unwrap()), + dock: project_panel.dock.unwrap(), + entry_spacing: project_panel.entry_spacing.unwrap(), + file_icons: project_panel.file_icons.unwrap(), + folder_icons: project_panel.folder_icons.unwrap(), + git_status: project_panel.git_status.unwrap(), + indent_size: project_panel.indent_size.unwrap(), + indent_guides: IndentGuidesSettings { + show: project_panel.indent_guides.unwrap().show.unwrap(), + }, + sticky_scroll: project_panel.sticky_scroll.unwrap(), + auto_reveal_entries: project_panel.auto_reveal_entries.unwrap(), + auto_fold_dirs: project_panel.auto_fold_dirs.unwrap(), + starts_open: project_panel.starts_open.unwrap(), + scrollbar: ScrollbarSettings { + show: project_panel + .scrollbar + .unwrap() + .show + .flatten() + .map(Into::into), + }, + show_diagnostics: project_panel.show_diagnostics.unwrap(), + hide_root: project_panel.hide_root.unwrap(), + drag_and_drop: project_panel.drag_and_drop.unwrap(), + } + } - fn load( - sources: SettingsSources, - _: &mut gpui::App, - ) -> anyhow::Result { - sources.json_merge() + fn refine(&mut self, content: &SettingsContent, _cx: &mut ui::App) { + let Some(project_panel) = content.project_panel.as_ref() else { + return; + }; + self.button.merge_from(&project_panel.button); + self.hide_gitignore + .merge_from(&project_panel.hide_gitignore); + self.default_width + .merge_from(&project_panel.default_width.map(px)); + self.dock.merge_from(&project_panel.dock); + self.entry_spacing.merge_from(&project_panel.entry_spacing); + self.file_icons.merge_from(&project_panel.file_icons); + self.folder_icons.merge_from(&project_panel.folder_icons); + self.git_status.merge_from(&project_panel.git_status); + self.indent_size.merge_from(&project_panel.indent_size); + self.sticky_scroll.merge_from(&project_panel.sticky_scroll); + self.auto_reveal_entries + .merge_from(&project_panel.auto_reveal_entries); + self.auto_fold_dirs + .merge_from(&project_panel.auto_fold_dirs); + self.starts_open.merge_from(&project_panel.starts_open); + self.show_diagnostics + .merge_from(&project_panel.show_diagnostics); + self.hide_root.merge_from(&project_panel.hide_root); + self.drag_and_drop.merge_from(&project_panel.drag_and_drop); + if let Some(show) = project_panel + .indent_guides + .as_ref() + .and_then(|indent| indent.show) + { + self.indent_guides.show = show; + } + if let Some(show) = project_panel + .scrollbar + .as_ref() + .and_then(|scrollbar| scrollbar.show) + { + self.scrollbar.show = show.map(Into::into) + } } - fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) { - vscode.bool_setting("explorer.excludeGitIgnore", &mut current.hide_gitignore); - vscode.bool_setting("explorer.autoReveal", &mut current.auto_reveal_entries); - vscode.bool_setting("explorer.compactFolders", &mut current.auto_fold_dirs); + fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) { + if let Some(hide_gitignore) = vscode.read_bool("explorer.excludeGitIgnore") { + current.project_panel.get_or_insert_default().hide_gitignore = Some(hide_gitignore); + } + if let Some(auto_reveal) = vscode.read_bool("explorer.autoReveal") { + current + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(auto_reveal); + } + if let Some(compact_folders) = vscode.read_bool("explorer.compactFolders") { + current.project_panel.get_or_insert_default().auto_fold_dirs = Some(compact_folders); + } if Some(false) == vscode.read_bool("git.decorations.enabled") { - current.git_status = Some(false); + current.project_panel.get_or_insert_default().git_status = Some(false); } if Some(false) == vscode.read_bool("problems.decorations.enabled") { - current.show_diagnostics = Some(ShowDiagnostics::Off); + current + .project_panel + .get_or_insert_default() + .show_diagnostics = Some(ShowDiagnostics::Off); } if let (Some(false), Some(false)) = ( vscode.read_bool("explorer.decorations.badges"), vscode.read_bool("explorer.decorations.colors"), ) { - current.git_status = Some(false); - current.show_diagnostics = Some(ShowDiagnostics::Off); + current.project_panel.get_or_insert_default().git_status = Some(false); + current + .project_panel + .get_or_insert_default() + .show_diagnostics = Some(ShowDiagnostics::Off); } } } diff --git a/crates/project_panel/src/project_panel_tests.rs b/crates/project_panel/src/project_panel_tests.rs index ad2a7d12ecce31cf1aa4458b3fd59e23f63ab08b..61684929a5e61e62c08d2f0e9d91def408448d8f 100644 --- a/crates/project_panel/src/project_panel_tests.rs +++ b/crates/project_panel/src/project_panel_tests.rs @@ -2,7 +2,7 @@ use super::*; use collections::HashSet; use gpui::{Empty, Entity, TestAppContext, VisualTestContext, WindowHandle}; use pretty_assertions::assert_eq; -use project::{FakeFs, WorktreeSettings}; +use project::FakeFs; use serde_json::json; use settings::SettingsStore; use std::path::{Path, PathBuf}; @@ -161,8 +161,8 @@ async fn test_exclusions_in_visible_list(cx: &mut gpui::TestAppContext) { init_test(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(vec!["**/.git".to_string(), "**/4/**".to_string()]); }); }); @@ -3343,11 +3343,12 @@ async fn test_autoreveal_and_gitignored_files(cx: &mut gpui::TestAppContext) { init_test_with_editor(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Some(Vec::new()); - }); - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = Some(false) + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(Vec::new()); + settings + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(false); }); }) }); @@ -3465,8 +3466,11 @@ async fn test_autoreveal_and_gitignored_files(cx: &mut gpui::TestAppContext) { cx.update(|_, cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = Some(true) + store.update_user_settings(cx, |settings| { + settings + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(true) }); }) }); @@ -3579,13 +3583,14 @@ async fn test_gitignored_and_always_included(cx: &mut gpui::TestAppContext) { init_test_with_editor(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Some(Vec::new()); - worktree_settings.file_scan_inclusions = + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(Vec::new()); + settings.project.worktree.file_scan_inclusions = Some(vec!["always_included_but_ignored_dir/*".to_string()]); - }); - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = Some(false) + settings + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(false) }); }) }); @@ -3654,8 +3659,11 @@ async fn test_gitignored_and_always_included(cx: &mut gpui::TestAppContext) { cx.run_until_parked(); cx.update(|_, cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = Some(true) + store.update_user_settings(cx, |settings| { + settings + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(true) }); }) }); @@ -3695,11 +3703,12 @@ async fn test_explicit_reveal(cx: &mut gpui::TestAppContext) { init_test_with_editor(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Some(Vec::new()); - }); - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = Some(false) + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(Vec::new()); + settings + .project_panel + .get_or_insert_default() + .auto_reveal_entries = Some(false) }); }) }); @@ -3896,8 +3905,8 @@ async fn test_creating_excluded_entries(cx: &mut gpui::TestAppContext) { init_test(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_settings| { - project_settings.file_scan_exclusions = + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]); }); }); @@ -6545,11 +6554,12 @@ fn init_test(cx: &mut TestAppContext) { Project::init_settings(cx); cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_fold_dirs = Some(false); - }); - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Some(Vec::new()); + store.update_user_settings(cx, |settings| { + settings + .project_panel + .get_or_insert_default() + .auto_fold_dirs = Some(false); + settings.project.worktree.file_scan_exclusions = Some(Vec::new()); }); }); }); @@ -6567,11 +6577,12 @@ fn init_test_with_editor(cx: &mut TestAppContext) { Project::init_settings(cx); cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_fold_dirs = Some(false); - }); - store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Some(Vec::new()); + store.update_user_settings(cx, |settings| { + settings + .project_panel + .get_or_insert_default() + .auto_fold_dirs = Some(false); + settings.project.worktree.file_scan_exclusions = Some(Vec::new()) }); }); }); diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index eff8f9ff60f348efd09bf3be4195a29ebe6c76af..b8a341fa05c5ed8bc964371737687dc83d04f9dc 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -107,6 +107,8 @@ pub struct SettingsContent { pub outline_panel: Option, + pub project_panel: Option, + /// Configuration for the Message Editor pub message_editor: Option, @@ -628,7 +630,7 @@ pub struct OutlinePanelSettingsContent { /// The position of outline panel /// /// Default: left - pub dock: Option, + pub dock: Option, /// Whether to show file icons in the outline panel. /// /// Default: true @@ -671,7 +673,7 @@ pub struct OutlinePanelSettingsContent { #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] -pub enum OutlinePanelDockPosition { +pub enum DockSide { Left, Right, } diff --git a/crates/settings/src/settings_content/workspace.rs b/crates/settings/src/settings_content/workspace.rs index a5fe638df3c60380abc761b51f9976d6aa82d8a3..09fa136ea3574bacbd5d3dc10c2651ca3ba41c38 100644 --- a/crates/settings/src/settings_content/workspace.rs +++ b/crates/settings/src/settings_content/workspace.rs @@ -4,7 +4,7 @@ use collections::HashMap; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::DockPosition; +use crate::{DockPosition, DockSide, ScrollbarSettingsContent, ShowIndentGuides}; #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)] pub struct WorkspaceSettingsContent { @@ -328,3 +328,93 @@ impl OnLastWindowClosed { } } } + +#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct ProjectPanelSettingsContent { + /// Whether to show the project panel button in the status bar. + /// + /// Default: true + pub button: Option, + /// Whether to hide gitignore files in the project panel. + /// + /// Default: false + pub hide_gitignore: Option, + /// Customize default width (in pixels) taken by project panel + /// + /// Default: 240 + pub default_width: Option, + /// The position of project panel + /// + /// Default: left + pub dock: Option, + /// Spacing between worktree entries in the project panel. + /// + /// Default: comfortable + pub entry_spacing: Option, + /// Whether to show file icons in the project panel. + /// + /// Default: true + pub file_icons: Option, + /// Whether to show folder icons or chevrons for directories in the project panel. + /// + /// Default: true + pub folder_icons: Option, + /// Whether to show the git status in the project panel. + /// + /// Default: true + pub git_status: Option, + /// Amount of indentation (in pixels) for nested items. + /// + /// Default: 20 + pub indent_size: Option, + /// Whether to reveal it in the project panel automatically, + /// when a corresponding project entry becomes active. + /// Gitignored entries are never auto revealed. + /// + /// Default: true + pub auto_reveal_entries: Option, + /// Whether to fold directories automatically + /// when directory has only one directory inside. + /// + /// Default: true + pub auto_fold_dirs: Option, + /// Whether the project panel should open on startup. + /// + /// Default: true + pub starts_open: Option, + /// Scrollbar-related settings + pub scrollbar: Option, + /// Which files containing diagnostic errors/warnings to mark in the project panel. + /// + /// Default: all + pub show_diagnostics: Option, + /// Settings related to indent guides in the project panel. + pub indent_guides: Option, + /// Whether to hide the root entry when only one folder is open in the window. + /// + /// Default: false + pub hide_root: Option, + /// Whether to stick parent directories at top of the project panel. + /// + /// Default: true + pub sticky_scroll: Option, + /// Whether to enable drag-and-drop operations in the project panel. + /// + /// Default: true + pub drag_and_drop: Option, +} + +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ProjectPanelEntrySpacing { + /// Comfortable spacing of entries. + #[default] + Comfortable, + /// The standard spacing of entries. + Standard, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct ProjectPanelIndentGuidesSettings { + pub show: Option, +}