project panel

Conrad Irwin created

Change summary

crates/project_panel/src/project_panel.rs          |  45 +-
crates/project_panel/src/project_panel_settings.rs | 252 ++++++---------
crates/project_panel/src/project_panel_tests.rs    |  81 ++--
crates/settings/src/settings_content.rs            |   6 
crates/settings/src/settings_content/workspace.rs  |  92 +++++
5 files changed, 270 insertions(+), 206 deletions(-)

Detailed changes

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::<ProjectPanelSettings>(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<PanelEvent> 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<Self>) {
-        settings::update_settings_file::<ProjectPanelSettings>(
-            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 {

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<ShowIndentGuides>,
-}
-
 #[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<ShowScrollbar>,
 }
 
-#[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<Option<ShowScrollbar>>,
-}
-
-/// 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<bool>,
-    /// Whether to hide gitignore files in the project panel.
-    ///
-    /// Default: false
-    pub hide_gitignore: Option<bool>,
-    /// Customize default width (in pixels) taken by project panel
-    ///
-    /// Default: 240
-    pub default_width: Option<f32>,
-    /// The position of project panel
-    ///
-    /// Default: left
-    pub dock: Option<ProjectPanelDockPosition>,
-    /// Spacing between worktree entries in the project panel.
-    ///
-    /// Default: comfortable
-    pub entry_spacing: Option<EntrySpacing>,
-    /// Whether to show file icons in the project panel.
-    ///
-    /// Default: true
-    pub file_icons: Option<bool>,
-    /// Whether to show folder icons or chevrons for directories in the project panel.
-    ///
-    /// Default: true
-    pub folder_icons: Option<bool>,
-    /// Whether to show the git status in the project panel.
-    ///
-    /// Default: true
-    pub git_status: Option<bool>,
-    /// Amount of indentation (in pixels) for nested items.
-    ///
-    /// Default: 20
-    pub indent_size: Option<f32>,
-    /// 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<bool>,
-    /// Whether to fold directories automatically
-    /// when directory has only one directory inside.
-    ///
-    /// Default: true
-    pub auto_fold_dirs: Option<bool>,
-    /// Whether the project panel should open on startup.
-    ///
-    /// Default: true
-    pub starts_open: Option<bool>,
-    /// Scrollbar-related settings
-    pub scrollbar: Option<ScrollbarSettingsContent>,
-    /// Which files containing diagnostic errors/warnings to mark in the project panel.
-    ///
-    /// Default: all
-    pub show_diagnostics: Option<ShowDiagnostics>,
-    /// Settings related to indent guides in the project panel.
-    pub indent_guides: Option<IndentGuidesSettingsContent>,
-    /// Whether to hide the root entry when only one folder is open in the window.
-    ///
-    /// Default: false
-    pub hide_root: Option<bool>,
-    /// Whether to stick parent directories at top of the project panel.
-    ///
-    /// Default: true
-    pub sticky_scroll: Option<bool>,
-    /// Whether to enable drag-and-drop operations in the project panel.
-    ///
-    /// Default: true
-    pub drag_and_drop: Option<bool>,
-}
-
 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<Self::FileContent>,
-        _: &mut gpui::App,
-    ) -> anyhow::Result<Self> {
-        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);
         }
     }
 }

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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
-                worktree_settings.file_scan_exclusions = Some(Vec::new());
-            });
-            store.update_user_settings::<ProjectPanelSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<ProjectPanelSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(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::<ProjectPanelSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<ProjectPanelSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
-                worktree_settings.file_scan_exclusions = Some(Vec::new());
-            });
-            store.update_user_settings::<ProjectPanelSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
-                project_panel_settings.auto_fold_dirs = Some(false);
-            });
-            store.update_user_settings::<WorktreeSettings>(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::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
-                project_panel_settings.auto_fold_dirs = Some(false);
-            });
-            store.update_user_settings::<WorktreeSettings>(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())
             });
         });
     });

crates/settings/src/settings_content.rs 🔗

@@ -107,6 +107,8 @@ pub struct SettingsContent {
 
     pub outline_panel: Option<OutlinePanelSettingsContent>,
 
+    pub project_panel: Option<ProjectPanelSettingsContent>,
+
     /// Configuration for the Message Editor
     pub message_editor: Option<MessageEditorSettings>,
 
@@ -628,7 +630,7 @@ pub struct OutlinePanelSettingsContent {
     /// The position of outline panel
     ///
     /// Default: left
-    pub dock: Option<OutlinePanelDockPosition>,
+    pub dock: Option<DockSide>,
     /// 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,
 }

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<bool>,
+    /// Whether to hide gitignore files in the project panel.
+    ///
+    /// Default: false
+    pub hide_gitignore: Option<bool>,
+    /// Customize default width (in pixels) taken by project panel
+    ///
+    /// Default: 240
+    pub default_width: Option<f32>,
+    /// The position of project panel
+    ///
+    /// Default: left
+    pub dock: Option<DockSide>,
+    /// Spacing between worktree entries in the project panel.
+    ///
+    /// Default: comfortable
+    pub entry_spacing: Option<ProjectPanelEntrySpacing>,
+    /// Whether to show file icons in the project panel.
+    ///
+    /// Default: true
+    pub file_icons: Option<bool>,
+    /// Whether to show folder icons or chevrons for directories in the project panel.
+    ///
+    /// Default: true
+    pub folder_icons: Option<bool>,
+    /// Whether to show the git status in the project panel.
+    ///
+    /// Default: true
+    pub git_status: Option<bool>,
+    /// Amount of indentation (in pixels) for nested items.
+    ///
+    /// Default: 20
+    pub indent_size: Option<f32>,
+    /// 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<bool>,
+    /// Whether to fold directories automatically
+    /// when directory has only one directory inside.
+    ///
+    /// Default: true
+    pub auto_fold_dirs: Option<bool>,
+    /// Whether the project panel should open on startup.
+    ///
+    /// Default: true
+    pub starts_open: Option<bool>,
+    /// Scrollbar-related settings
+    pub scrollbar: Option<ScrollbarSettingsContent>,
+    /// Which files containing diagnostic errors/warnings to mark in the project panel.
+    ///
+    /// Default: all
+    pub show_diagnostics: Option<ShowDiagnostics>,
+    /// Settings related to indent guides in the project panel.
+    pub indent_guides: Option<ProjectPanelIndentGuidesSettings>,
+    /// Whether to hide the root entry when only one folder is open in the window.
+    ///
+    /// Default: false
+    pub hide_root: Option<bool>,
+    /// Whether to stick parent directories at top of the project panel.
+    ///
+    /// Default: true
+    pub sticky_scroll: Option<bool>,
+    /// Whether to enable drag-and-drop operations in the project panel.
+    ///
+    /// Default: true
+    pub drag_and_drop: Option<bool>,
+}
+
+#[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<ShowIndentGuides>,
+}