Basic side-by-side diff implementation (#43586)

Release Notes:

- N/A

---------

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Co-authored-by: Cameron <cameron@zed.dev>
This commit is contained in:
Cole Miller
2025-11-30 22:45:01 -05:00
committed by GitHub
co-authored by cameron Cameron
parent ca6e64d451
commit 2e00f40c54
29 changed files with 1915 additions and 518 deletions
+205 -1
View File
@@ -35,7 +35,9 @@ use language_settings::Formatter;
use languages::markdown_lang;
use languages::rust_lang;
use lsp::CompletionParams;
use multi_buffer::{IndentGuide, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey};
use multi_buffer::{
IndentGuide, MultiBufferFilterMode, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey,
};
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_ne};
use project::{
@@ -28199,6 +28201,208 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
"});
}
#[gpui::test]
async fn test_filtered_editor_pair(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut leader_cx = EditorTestContext::new(cx).await;
let diff_base = indoc!(
r#"
one
two
three
four
five
six
"#
);
let initial_state = indoc!(
r#"
ˇone
two
THREE
four
five
six
"#
);
leader_cx.set_state(initial_state);
leader_cx.set_head_text(&diff_base);
leader_cx.run_until_parked();
let follower = leader_cx.update_multibuffer(|leader, cx| {
leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
leader.set_all_diff_hunks_expanded(cx);
leader.get_or_create_follower(cx)
});
follower.update(cx, |follower, cx| {
follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
follower.set_all_diff_hunks_expanded(cx);
});
let follower_editor =
leader_cx.new_window_entity(|window, cx| build_editor(follower, window, cx));
// leader_cx.window.focus(&follower_editor.focus_handle(cx));
let mut follower_cx = EditorTestContext::for_editor_in(follower_editor, &mut leader_cx).await;
cx.run_until_parked();
leader_cx.assert_editor_state(initial_state);
follower_cx.assert_editor_state(indoc! {
r#"
ˇone
two
three
four
five
six
"#
});
follower_cx.editor(|editor, _window, cx| {
assert!(editor.read_only(cx));
});
leader_cx.update_editor(|editor, _window, cx| {
editor.edit([(Point::new(4, 0)..Point::new(5, 0), "FIVE\n")], cx);
});
cx.run_until_parked();
leader_cx.assert_editor_state(indoc! {
r#"
ˇone
two
THREE
four
FIVE
six
"#
});
follower_cx.assert_editor_state(indoc! {
r#"
ˇone
two
three
four
five
six
"#
});
leader_cx.update_editor(|editor, _window, cx| {
editor.edit([(Point::new(6, 0)..Point::new(6, 0), "SEVEN")], cx);
});
cx.run_until_parked();
leader_cx.assert_editor_state(indoc! {
r#"
ˇone
two
THREE
four
FIVE
six
SEVEN"#
});
follower_cx.assert_editor_state(indoc! {
r#"
ˇone
two
three
four
five
six
"#
});
leader_cx.update_editor(|editor, window, cx| {
editor.move_down(&MoveDown, window, cx);
editor.refresh_selected_text_highlights(true, window, cx);
});
leader_cx.run_until_parked();
}
#[gpui::test]
async fn test_filtered_editor_pair_complex(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let base_text = "base\n";
let buffer_text = "buffer\n";
let buffer1 = cx.new(|cx| Buffer::local(buffer_text, cx));
let diff1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer1, cx));
let extra_buffer_1 = cx.new(|cx| Buffer::local("dummy text 1\n", cx));
let extra_diff_1 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_1, cx));
let extra_buffer_2 = cx.new(|cx| Buffer::local("dummy text 2\n", cx));
let extra_diff_2 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_2, cx));
let leader = cx.new(|cx| {
let mut leader = MultiBuffer::new(Capability::ReadWrite);
leader.set_all_diff_hunks_expanded(cx);
leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
leader
});
let follower = leader.update(cx, |leader, cx| leader.get_or_create_follower(cx));
follower.update(cx, |follower, _| {
follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
});
leader.update(cx, |leader, cx| {
leader.insert_excerpts_after(
ExcerptId::min(),
extra_buffer_2.clone(),
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
cx,
);
leader.add_diff(extra_diff_2.clone(), cx);
leader.insert_excerpts_after(
ExcerptId::min(),
extra_buffer_1.clone(),
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
cx,
);
leader.add_diff(extra_diff_1.clone(), cx);
leader.insert_excerpts_after(
ExcerptId::min(),
buffer1.clone(),
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
cx,
);
leader.add_diff(diff1.clone(), cx);
});
cx.run_until_parked();
let mut cx = cx.add_empty_window();
let leader_editor = cx
.new_window_entity(|window, cx| Editor::for_multibuffer(leader.clone(), None, window, cx));
let follower_editor = cx.new_window_entity(|window, cx| {
Editor::for_multibuffer(follower.clone(), None, window, cx)
});
let mut leader_cx = EditorTestContext::for_editor_in(leader_editor.clone(), &mut cx).await;
leader_cx.assert_editor_state(indoc! {"
ˇbuffer
dummy text 1
dummy text 2
"});
let mut follower_cx = EditorTestContext::for_editor_in(follower_editor.clone(), &mut cx).await;
follower_cx.assert_editor_state(indoc! {"
ˇbase
"});
}
#[gpui::test]
async fn test_multibuffer_scroll_cursor_top_margin(cx: &mut TestAppContext) {
init_test(cx, |_| {});