From 5a202db287e1560f16833ca785e3da3b431e4173 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 6 Apr 2026 12:36:51 -0400 Subject: [PATCH] git: Fix out-of-bounds indexing when diff bases contain CRLF (cherry-pick #52605) (#53251) Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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 Release Notes: - Fixed a crash on windows with diffs. --- crates/buffer_diff/src/buffer_diff.rs | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 2ea1c349074dcadf9723e11df154f3d9e9bf3d75..a1acefe2f315b29e9a321177e4395ead337fe06a 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -1651,6 +1651,7 @@ impl BufferDiff { language: Option>, cx: &App, ) -> Task { + let base_text = base_text.map(|t| text::LineEnding::normalize_arc(t)); let prev_base_text = self.base_text(cx).as_rope().clone(); let base_text_changed = base_text_change.is_some(); let compute_base_text_edits = base_text_change == Some(true); @@ -3947,4 +3948,36 @@ mod tests { } } } + + #[gpui::test] + async fn test_set_base_text_with_crlf(cx: &mut gpui::TestAppContext) { + let base_text_crlf = "one\r\ntwo\r\nthree\r\nfour\r\nfive\r\n"; + let base_text_lf = "one\ntwo\nthree\nfour\nfive\n"; + assert_ne!(base_text_crlf.len(), base_text_lf.len()); + + let buffer_text = "one\nTWO\nthree\nfour\nfive\n"; + let buffer = Buffer::new( + ReplicaId::LOCAL, + BufferId::new(1).unwrap(), + buffer_text.to_string(), + ); + let buffer_snapshot = buffer.snapshot(); + + let diff = cx.new(|cx| BufferDiff::new(&buffer_snapshot, cx)); + diff.update(cx, |diff, cx| { + diff.set_base_text( + Some(Arc::from(base_text_crlf)), + None, + buffer_snapshot.clone(), + cx, + ) + }) + .await + .ok(); + cx.run_until_parked(); + + let snapshot = diff.update(cx, |diff, cx| diff.snapshot(cx)); + snapshot.buffer_point_to_base_text_range(Point::new(0, 0), &buffer_snapshot); + snapshot.buffer_point_to_base_text_range(Point::new(1, 0), &buffer_snapshot); + } }