git: Add a setting for the default view mode of `SplittableEditor` (#48440)

Cole Miller created

Release Notes:

- N/A

Change summary

assets/settings/default.json          |  4 +++
crates/editor/src/editor.rs           |  9 +++++--
crates/editor/src/editor_settings.rs  | 10 +++++---
crates/editor/src/split.rs            | 31 +++++++++++++++++++-------
crates/git_ui/src/project_diff.rs     |  5 ++-
crates/settings/src/vscode_import.rs  |  1 
crates/settings_content/src/editor.rs | 33 +++++++++++++++++++++++++++++
7 files changed, 75 insertions(+), 18 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -291,6 +291,10 @@
   "completion_menu_scrollbar": "never",
   // Whether to align detail text in code completions context menus left or right.
   "completion_detail_alignment": "left",
+  // How to display diffs in the editor.
+  //
+  // Default: stacked
+  "diff_view_style": "stacked",
   // Show method signatures in the editor, when inside parentheses.
   "auto_signature_help": false,
   // Whether to show the signature help after completion or a bracket pair inserted.

crates/editor/src/editor.rs 🔗

@@ -59,8 +59,9 @@ pub use display_map::{
 };
 pub use edit_prediction_types::Direction;
 pub use editor_settings::{
-    CompletionDetailAlignment, CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings,
-    HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap,
+    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
+    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
+    ShowMinimap,
 };
 pub use element::{
     CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
@@ -77,7 +78,9 @@ pub use multi_buffer::{
     MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
     ToPoint,
 };
-pub use split::{SplitDiffFeatureFlag, SplittableEditor, ToggleLockedCursors, ToggleSplitDiff};
+pub use split::{
+    SplitDiff, SplitDiffFeatureFlag, SplittableEditor, ToggleLockedCursors, ToggleSplitDiff,
+};
 pub use split_editor_view::SplitEditorView;
 pub use text::Bias;
 

crates/editor/src/editor_settings.rs 🔗

@@ -4,10 +4,10 @@ use gpui::App;
 use language::CursorShape;
 use project::project_settings::DiagnosticSeverity;
 pub use settings::{
-    CompletionDetailAlignment, CurrentLineHighlight, DelayMs, DisplayIn, DocumentColorsRenderMode,
-    DoubleClickInMultibuffer, GoToDefinitionFallback, HideMouseMode, MinimapThumb,
-    MinimapThumbBorder, MultiCursorModifier, ScrollBeyondLastLine, ScrollbarDiagnostics,
-    SeedQuerySetting, ShowMinimap, SnippetSortOrder,
+    CompletionDetailAlignment, CurrentLineHighlight, DelayMs, DiffViewStyle, DisplayIn,
+    DocumentColorsRenderMode, DoubleClickInMultibuffer, GoToDefinitionFallback, HideMouseMode,
+    MinimapThumb, MinimapThumbBorder, MultiCursorModifier, ScrollBeyondLastLine,
+    ScrollbarDiagnostics, SeedQuerySetting, ShowMinimap, SnippetSortOrder,
 };
 use settings::{RegisterSetting, RelativeLineNumbers, Settings};
 use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
@@ -59,6 +59,7 @@ pub struct EditorSettings {
     pub minimum_contrast_for_highlights: f32,
     pub completion_menu_scrollbar: ShowScrollbar,
     pub completion_detail_alignment: CompletionDetailAlignment,
+    pub diff_view_style: DiffViewStyle,
 }
 #[derive(Debug, Clone)]
 pub struct Jupyter {
@@ -289,6 +290,7 @@ impl Settings for EditorSettings {
             minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap().0,
             completion_menu_scrollbar: editor.completion_menu_scrollbar.map(Into::into).unwrap(),
             completion_detail_alignment: editor.completion_detail_alignment.unwrap(),
+            diff_view_style: editor.diff_view_style.unwrap(),
         }
     }
 }

crates/editor/src/split.rs 🔗

@@ -15,6 +15,7 @@ use multi_buffer::{
 };
 use project::Project;
 use rope::Point;
+use settings::DiffViewStyle;
 use text::{BufferId, OffsetRangeExt as _, Patch, ToPoint as _};
 use ui::{
     App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render,
@@ -332,7 +333,7 @@ impl FeatureFlag for SplitDiffFeatureFlag {
 
 #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]
 #[action(namespace = editor)]
-struct SplitDiff;
+pub struct SplitDiff;
 
 #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]
 #[action(namespace = editor)]
@@ -408,7 +409,8 @@ impl SplittableEditor {
         }
     }
 
-    pub fn new_unsplit(
+    pub fn new(
+        style: DiffViewStyle,
         rhs_multibuffer: Entity<MultiBuffer>,
         project: Entity<Project>,
         workspace: Entity<Workspace>,
@@ -441,17 +443,26 @@ impl SplittableEditor {
             }),
         ];
 
+        let this = cx.weak_entity();
         window.defer(cx, {
             let workspace = workspace.downgrade();
             let rhs_editor = rhs_editor.downgrade();
             move |window, cx| {
                 workspace
                     .update(cx, |workspace, cx| {
-                        rhs_editor.update(cx, |editor, cx| {
-                            editor.added_to_workspace(workspace, window, cx);
-                        })
+                        rhs_editor
+                            .update(cx, |editor, cx| {
+                                editor.added_to_workspace(workspace, window, cx);
+                            })
+                            .ok();
+                    })
+                    .ok();
+                if style == DiffViewStyle::SideBySide {
+                    this.update(cx, |this, cx| {
+                        this.split(&Default::default(), window, cx);
                     })
                     .ok();
+                }
             }
         });
         let split_state = cx.new(|cx| SplitEditorState::new(cx));
@@ -466,7 +477,7 @@ impl SplittableEditor {
         }
     }
 
-    fn split(&mut self, _: &SplitDiff, window: &mut Window, cx: &mut Context<Self>) {
+    pub fn split(&mut self, _: &SplitDiff, window: &mut Window, cx: &mut Context<Self>) {
         if !cx.has_flag::<SplitDiffFeatureFlag>() {
             return;
         }
@@ -2003,6 +2014,8 @@ impl LhsEditor {
 
 #[cfg(test)]
 mod tests {
+    use std::sync::Arc;
+
     use buffer_diff::BufferDiff;
     use collections::HashSet;
     use fs::FakeFs;
@@ -2014,8 +2027,7 @@ mod tests {
     use pretty_assertions::assert_eq;
     use project::Project;
     use rand::rngs::StdRng;
-    use settings::SettingsStore;
-    use std::sync::Arc;
+    use settings::{DiffViewStyle, SettingsStore};
     use ui::{VisualContext as _, div, px};
     use workspace::Workspace;
 
@@ -2043,7 +2055,8 @@ mod tests {
             multibuffer
         });
         let editor = cx.new_window_entity(|window, cx| {
-            let mut editor = SplittableEditor::new_unsplit(
+            let mut editor = SplittableEditor::new(
+                DiffViewStyle::Stacked,
                 rhs_multibuffer.clone(),
                 project.clone(),
                 workspace,

crates/git_ui/src/project_diff.rs 🔗

@@ -9,7 +9,7 @@ use anyhow::{Context as _, Result, anyhow};
 use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
 use collections::{HashMap, HashSet};
 use editor::{
-    Addon, Editor, EditorEvent, SelectionEffects, SplittableEditor,
+    Addon, Editor, EditorEvent, EditorSettings, SelectionEffects, SplittableEditor,
     actions::{GoToHunk, GoToPreviousHunk, SendReviewToAgent},
     multibuffer_context_lines,
     scroll::Autoscroll,
@@ -300,7 +300,8 @@ impl ProjectDiff {
         });
 
         let editor = cx.new(|cx| {
-            let diff_display_editor = SplittableEditor::new_unsplit(
+            let diff_display_editor = SplittableEditor::new(
+                EditorSettings::get_global(cx).diff_view_style,
                 multibuffer.clone(),
                 project.clone(),
                 workspace.clone(),

crates/settings/src/vscode_import.rs 🔗

@@ -307,6 +307,7 @@ impl VsCodeSettings {
             vertical_scroll_margin: self.read_f32("editor.cursorSurroundingLines"),
             completion_menu_scrollbar: None,
             completion_detail_alignment: None,
+            diff_view_style: None,
         }
     }
 

crates/settings_content/src/editor.rs 🔗

@@ -221,6 +221,11 @@ pub struct EditorSettingsContent {
     ///
     /// Default: left
     pub completion_detail_alignment: Option<CompletionDetailAlignment>,
+
+    /// How to display diffs in the editor.
+    ///
+    /// Default: stacked
+    pub diff_view_style: Option<DiffViewStyle>,
 }
 
 #[derive(
@@ -779,6 +784,34 @@ pub enum SnippetSortOrder {
     None,
 }
 
+/// How to display diffs in the editor.
+///
+/// Default: stacked
+#[derive(
+    Copy,
+    Clone,
+    Debug,
+    Default,
+    PartialEq,
+    Eq,
+    Serialize,
+    Deserialize,
+    JsonSchema,
+    MergeFrom,
+    strum::Display,
+    strum::EnumIter,
+    strum::VariantArray,
+    strum::VariantNames,
+)]
+#[serde(rename_all = "snake_case")]
+pub enum DiffViewStyle {
+    /// Show diffs in a single stacked view.
+    #[default]
+    Stacked,
+    /// Show diffs in a side-by-side split view.
+    SideBySide,
+}
+
 /// Default options for buffer and project search items.
 #[with_fallible_options]
 #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]