From b1e8473723c8bd30fc6eddfc015ca0f35be6b016 Mon Sep 17 00:00:00 2001 From: Om Chillure Date: Mon, 23 Mar 2026 14:40:18 +0530 Subject: [PATCH] git_ui: Support side-by-side diff view in clipboard selection diff (#51966) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### Context Switches `TextDiffView` from using `Editor` directly to `SplittableEditor`, enabling side-by-side diff view support for "Diff Clipboard with Selection". The diff view now respects the user's `diff_view_style` setting. Split out from #51457. This PR contains only the `SplittableEditor` wiring. The multibuffer coordinate fix for non-singleton editors will follow in a separate PR. Closes #50912 (partially) #### How to Review Small PR — all changes are in `crates/git_ui/src/text_diff_view.rs`. Focus on: - `new()`: `SplittableEditor::new` replaces `Editor::for_multibuffer`, editor-specific setup goes through `rhs_editor()` - Item trait delegation: `act_as_type`, `for_each_project_item`, `set_nav_history` updated for `SplittableEditor` - Tests: pinned `DiffViewStyle::Unified` and assertions go through `rhs_editor()` #### Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable #### Video : [Screencast from 2026-03-19 23-11-36.webm](https://github.com/user-attachments/assets/c5a2381d-238d-43ef-ac6f-9994996c0c69) #### Release Notes: - Improved "Diff Clipboard with Selection" to support side-by-side diff view style. --- crates/git_ui/src/text_diff_view.rs | 61 ++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/crates/git_ui/src/text_diff_view.rs b/crates/git_ui/src/text_diff_view.rs index ec2d6d7813da5883f0a146ff47af5027c3b7f643..965f41030817d3b7434a6fd02fb3a2de18046823 100644 --- a/crates/git_ui/src/text_diff_view.rs +++ b/crates/git_ui/src/text_diff_view.rs @@ -2,7 +2,10 @@ use anyhow::Result; use buffer_diff::BufferDiff; -use editor::{Editor, EditorEvent, MultiBuffer, ToPoint, actions::DiffClipboardWithSelectionData}; +use editor::{ + Editor, EditorEvent, EditorSettings, MultiBuffer, SplittableEditor, ToPoint, + actions::DiffClipboardWithSelectionData, +}; use futures::{FutureExt, select_biased}; use gpui::{ AnyElement, App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FocusHandle, @@ -10,6 +13,7 @@ use gpui::{ }; use language::{self, Buffer, Point}; use project::Project; +use settings::Settings; use std::{ any::{Any, TypeId}, cmp, @@ -22,13 +26,13 @@ use ui::{Color, Icon, IconName, Label, LabelCommon as _, SharedString}; use util::paths::PathExt; use workspace::{ - Item, ItemHandle as _, ItemNavHistory, Workspace, + Item, ItemNavHistory, Workspace, item::{ItemEvent, SaveOptions, TabContentParams}, searchable::SearchableItemHandle, }; pub struct TextDiffView { - diff_editor: Entity, + diff_editor: Entity, title: SharedString, path: Option, buffer_changes_tx: watch::Sender<()>, @@ -125,11 +129,11 @@ impl TextDiffView { ); let task = window.spawn(cx, async move |cx| { - let project = workspace.update(cx, |workspace, _| workspace.project().clone())?; - update_diff_buffer(&diff_buffer, &source_buffer, &clipboard_buffer, cx).await?; workspace.update_in(cx, |workspace, window, cx| { + let project = workspace.project().clone(); + let workspace_entity = cx.entity(); let diff_view = cx.new(|cx| { TextDiffView::new( clipboard_buffer, @@ -138,6 +142,7 @@ impl TextDiffView { expanded_selection_range, diff_buffer, project, + workspace_entity, window, cx, ) @@ -162,6 +167,7 @@ impl TextDiffView { source_range: Range, diff_buffer: Entity, project: Entity, + workspace: Entity, window: &mut Window, cx: &mut Context, ) -> Self { @@ -174,15 +180,24 @@ impl TextDiffView { multibuffer }); let diff_editor = cx.new(|cx| { - let mut editor = Editor::for_multibuffer(multibuffer, Some(project), window, cx); - editor.start_temporary_diff_override(); - editor.disable_diagnostics(cx); - editor.set_expand_all_diff_hunks(cx); - editor.set_render_diff_hunk_controls( + let splittable = SplittableEditor::new( + EditorSettings::get_global(cx).diff_view_style, + multibuffer, + project, + workspace, + window, + cx, + ); + splittable.set_render_diff_hunk_controls( Arc::new(|_, _, _, _, _, _, _, _| gpui::Empty.into_any_element()), cx, ); - editor + splittable.rhs_editor().update(cx, |editor, cx| { + editor.start_temporary_diff_override(); + editor.disable_diagnostics(cx); + editor.set_expand_all_diff_hunks(cx); + }); + splittable }); let (buffer_changes_tx, mut buffer_changes_rx) = watch::channel(()); @@ -352,12 +367,14 @@ impl Item for TextDiffView { &'a self, type_id: TypeId, self_handle: &'a Entity, - _: &'a App, + cx: &'a App, ) -> Option { if type_id == TypeId::of::() { Some(self_handle.clone().into()) - } else if type_id == TypeId::of::() { + } else if type_id == TypeId::of::() { Some(self.diff_editor.clone().into()) + } else if type_id == TypeId::of::() { + Some(self.diff_editor.read(cx).rhs_editor().clone().into()) } else { None } @@ -372,7 +389,7 @@ impl Item for TextDiffView { cx: &App, f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem), ) { - self.diff_editor.for_each_project_item(cx, f) + self.diff_editor.read(cx).for_each_project_item(cx, f) } fn set_nav_history( @@ -381,7 +398,8 @@ impl Item for TextDiffView { _: &mut Window, cx: &mut Context, ) { - self.diff_editor.update(cx, |editor, _| { + let rhs = self.diff_editor.read(cx).rhs_editor().clone(); + rhs.update(cx, |editor, _| { editor.set_nav_history(Some(nav_history)); }); } @@ -463,11 +481,11 @@ impl Render for TextDiffView { mod tests { use super::*; use editor::{MultiBufferOffset, PathKey, test::editor_test_context::assert_state_with_diff}; - use gpui::{TestAppContext, VisualContext}; + use gpui::{BorrowAppContext, TestAppContext, VisualContext}; use language::Point; use project::{FakeFs, Project}; use serde_json::json; - use settings::SettingsStore; + use settings::{DiffViewStyle, SettingsStore}; use unindent::unindent; use util::{path, test::marked_text_ranges}; use workspace::MultiWorkspace; @@ -476,6 +494,11 @@ mod tests { cx.update(|cx| { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); + cx.update_global::(|store, cx| { + store.update_user_settings(cx, |settings| { + settings.editor.diff_view_style = Some(DiffViewStyle::Unified); + }); + }); theme::init(theme::LoadThemes::JustBase, cx); }); } @@ -918,7 +941,9 @@ mod tests { cx.executor().run_until_parked(); assert_state_with_diff( - &diff_view.read_with(cx, |diff_view, _| diff_view.diff_editor.clone()), + &diff_view.read_with(cx, |diff_view, cx| { + diff_view.diff_editor.read(cx).rhs_editor().clone() + }), cx, expected_diff, );