From 4139e2de235d9cdad9ecc6421e8f2a00ba8ff976 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 8 Oct 2024 08:58:28 -0700 Subject: [PATCH] In proposed change editors, apply diff hunks in batches (#18841) Release Notes: - N/A --- crates/editor/src/editor.rs | 14 +++++++--- crates/editor/src/hunk_diff.rs | 4 +-- crates/language/src/buffer.rs | 40 +++++++++++++++++++++-------- crates/language/src/buffer_tests.rs | 14 +++++----- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d06f66184b4b4d23617fc637de98f64d970d41d4..39bc2f45e551ab60431d4765934c027b146dc5fe 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6213,14 +6213,22 @@ impl Editor { fn apply_selected_diff_hunks(&mut self, _: &ApplyDiffHunk, cx: &mut ViewContext) { let snapshot = self.buffer.read(cx).snapshot(cx); let hunks = hunks_for_selections(&snapshot, &self.selections.disjoint_anchors()); + let mut ranges_by_buffer = HashMap::default(); self.transact(cx, |editor, cx| { for hunk in hunks { if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) { - buffer.update(cx, |buffer, cx| { - buffer.merge_into_base(Some(hunk.buffer_range.to_offset(buffer)), cx); - }); + ranges_by_buffer + .entry(buffer.clone()) + .or_insert_with(Vec::new) + .push(hunk.buffer_range.to_offset(buffer.read(cx))); } } + + for (buffer, ranges) in ranges_by_buffer { + buffer.update(cx, |buffer, cx| { + buffer.merge_into_base(ranges, cx); + }); + } }); } diff --git a/crates/editor/src/hunk_diff.rs b/crates/editor/src/hunk_diff.rs index 7fbb07ae35c79e694cc6775930912b36638918b5..e495481323a6f45d7a3743fefa83cad45bd1a4ae 100644 --- a/crates/editor/src/hunk_diff.rs +++ b/crates/editor/src/hunk_diff.rs @@ -350,7 +350,7 @@ impl Editor { .next()?; buffer.update(cx, |branch_buffer, cx| { - branch_buffer.merge_into_base(Some(range), cx); + branch_buffer.merge_into_base(vec![range], cx); }); None @@ -360,7 +360,7 @@ impl Editor { let buffers = self.buffer.read(cx).all_buffers(); for branch_buffer in buffers { branch_buffer.update(cx, |branch_buffer, cx| { - branch_buffer.merge_into_base(None, cx); + branch_buffer.merge_into_base(Vec::new(), cx); }); } } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 160e8b3ba9aa96d3e7dc7c55ec7287ebc472fcec..59740509d3677c99499946496f82db1ebda558e9 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -827,25 +827,45 @@ impl Buffer { }) } - /// Applies all of the changes in this buffer that intersect the given `range` - /// to its base buffer. This buffer must be a branch buffer to call this method. - pub fn merge_into_base(&mut self, range: Option>, cx: &mut ModelContext) { + /// Applies all of the changes in this buffer that intersect any of the + /// given `ranges` to its base buffer. + /// + /// If `ranges` is empty, then all changes will be applied. This buffer must + /// be a branch buffer to call this method. + pub fn merge_into_base(&mut self, ranges: Vec>, cx: &mut ModelContext) { let Some(base_buffer) = self.diff_base_buffer() else { debug_panic!("not a branch buffer"); return; }; + let mut ranges = if ranges.is_empty() { + &[0..usize::MAX] + } else { + ranges.as_slice() + } + .into_iter() + .peekable(); + let mut edits = Vec::new(); for edit in self.edits_since::(&base_buffer.read(cx).version()) { - if let Some(range) = &range { - if range.start > edit.new.end || edit.new.start > range.end { - continue; + let mut is_included = false; + while let Some(range) = ranges.peek() { + if range.end < edit.new.start { + ranges.next().unwrap(); + } else { + if range.start <= edit.new.end { + is_included = true; + } + break; } } - edits.push(( - edit.old.clone(), - self.text_for_range(edit.new.clone()).collect::(), - )); + + if is_included { + edits.push(( + edit.old.clone(), + self.text_for_range(edit.new.clone()).collect::(), + )); + } } let operation = base_buffer.update(cx, |base_buffer, cx| { diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index fe390d55101b0b94727b0368df954c2b1edec9fc..83c35cbeca56bdb32efa347dd85afe8e03561983 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -2472,7 +2472,7 @@ fn test_branch_and_merge(cx: &mut TestAppContext) { // Merging the branch applies all of its changes to the base. branch_buffer.update(cx, |branch_buffer, cx| { - branch_buffer.merge_into_base(None, cx); + branch_buffer.merge_into_base(Vec::new(), cx); }); branch_buffer.update(cx, |branch_buffer, cx| { @@ -2494,7 +2494,7 @@ fn test_merge_into_base(cx: &mut TestAppContext) { // Make 3 edits, merge one into the base. branch.update(cx, |branch, cx| { branch.edit([(0..3, "ABC"), (7..9, "HI"), (11..11, "LMN")], None, cx); - branch.merge_into_base(Some(5..8), cx); + branch.merge_into_base(vec![5..8], cx); }); branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjkLMN")); @@ -2503,13 +2503,13 @@ fn test_merge_into_base(cx: &mut TestAppContext) { // Undo the one already-merged edit. Merge that into the base. branch.update(cx, |branch, cx| { branch.edit([(7..9, "hi")], None, cx); - branch.merge_into_base(Some(5..8), cx); + branch.merge_into_base(vec![5..8], cx); }); base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk")); // Merge an insertion into the base. branch.update(cx, |branch, cx| { - branch.merge_into_base(Some(11..11), cx); + branch.merge_into_base(vec![11..11], cx); }); branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefghijkLMN")); @@ -2518,7 +2518,7 @@ fn test_merge_into_base(cx: &mut TestAppContext) { // Deleted the inserted text and merge that into the base. branch.update(cx, |branch, cx| { branch.edit([(11..14, "")], None, cx); - branch.merge_into_base(Some(10..11), cx); + branch.merge_into_base(vec![10..11], cx); }); base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk")); @@ -2534,7 +2534,7 @@ fn test_undo_after_merge_into_base(cx: &mut TestAppContext) { // Make 2 edits, merge one into the base. branch.update(cx, |branch, cx| { branch.edit([(0..3, "ABC"), (7..9, "HI")], None, cx); - branch.merge_into_base(Some(7..7), cx); + branch.merge_into_base(vec![7..7], cx); }); base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk")); branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk")); @@ -2548,7 +2548,7 @@ fn test_undo_after_merge_into_base(cx: &mut TestAppContext) { // Merge that operation into the base again. branch.update(cx, |branch, cx| { - branch.merge_into_base(Some(7..7), cx); + branch.merge_into_base(vec![7..7], cx); }); base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk")); branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));