@@ -6,42 +6,19 @@ use language::{
language_settings::language_settings, word_diff_ranges,
};
use rope::Rope;
-use std::{cmp::Ordering, future::Future, iter, ops::Range, sync::Arc};
+use std::{
+ cmp::Ordering,
+ future::Future,
+ iter,
+ ops::{Range, RangeInclusive},
+ sync::Arc,
+};
use sum_tree::SumTree;
use text::{
Anchor, Bias, BufferId, Edit, OffsetRangeExt, Patch, Point, ToOffset as _, ToPoint as _,
};
use util::ResultExt;
-fn translate_point_through_patch(
- patch: &Patch<Point>,
- point: Point,
-) -> (Range<Point>, Range<Point>) {
- let edits = patch.edits();
-
- let ix = match edits.binary_search_by(|probe| probe.old.start.cmp(&point)) {
- Ok(ix) => ix,
- Err(ix) => {
- if ix == 0 {
- return (point..point, point..point);
- } else {
- ix - 1
- }
- }
- };
-
- if let Some(edit) = edits.get(ix) {
- if point > edit.old.end {
- let translated = edit.new.end + (point - edit.old.end);
- (translated..translated, point..point)
- } else {
- (edit.new.start..edit.new.end, edit.old.start..edit.old.end)
- }
- } else {
- (point..point, point..point)
- }
-}
-
pub const MAX_WORD_DIFF_LINE_COUNT: usize = 5;
pub struct BufferDiff {
@@ -430,15 +407,15 @@ impl BufferDiffSnapshot {
result
}
- pub fn points_to_base_text_points<'a>(
+ /// Returns a patch mapping the provided main buffer snapshot to the base text of this diff.
+ ///
+ /// The returned patch is guaranteed to be accurate for all main buffer points in the provided range,
+ /// but not necessarily for points outside that range.
+ pub fn patch_for_buffer_range<'a>(
&'a self,
- points: impl IntoIterator<Item = Point> + 'a,
+ _range: RangeInclusive<Point>,
buffer: &'a text::BufferSnapshot,
- ) -> (
- impl 'a + Iterator<Item = Range<Point>>,
- Option<Range<Point>>,
- Option<(Point, Range<Point>)>,
- ) {
+ ) -> Patch<Point> {
let original_snapshot = self.original_buffer_snapshot();
let edits_since: Vec<Edit<Point>> = buffer
@@ -447,7 +424,7 @@ impl BufferDiffSnapshot {
let mut inverted_edits_since = Patch::new(edits_since);
inverted_edits_since.invert();
- let composed = inverted_edits_since.compose(
+ inverted_edits_since.compose(
self.inner
.hunks
.iter()
@@ -475,42 +452,18 @@ impl BufferDiffSnapshot {
None
},
),
- );
-
- let mut points = points.into_iter().peekable();
-
- let first_group = points.peek().map(|point| {
- let (_, old_range) = translate_point_through_patch(&composed, *point);
- old_range
- });
-
- let prev_boundary = points.peek().and_then(|first_point| {
- if first_point.row > 0 {
- let prev_point = Point::new(first_point.row - 1, 0);
- let (range, _) = translate_point_through_patch(&composed, prev_point);
- Some((prev_point, range))
- } else {
- None
- }
- });
-
- let iter = points.map(move |point| {
- let (range, _) = translate_point_through_patch(&composed, point);
- range
- });
-
- (iter, first_group, prev_boundary)
+ )
}
- pub fn base_text_points_to_points<'a>(
+ /// Returns a patch mapping the base text of this diff to the provided buffer snapshot.
+ ///
+ /// The returned patch is guaranteed to be accurate for all base text points in the provided range,
+ /// but not necessarily for points outside that range.
+ pub fn patch_for_base_text_range<'a>(
&'a self,
- points: impl IntoIterator<Item = Point> + 'a,
+ _range: RangeInclusive<Point>,
buffer: &'a text::BufferSnapshot,
- ) -> (
- impl 'a + Iterator<Item = Range<Point>>,
- Option<Range<Point>>,
- Option<(Point, Range<Point>)>,
- ) {
+ ) -> Patch<Point> {
let original_snapshot = self.original_buffer_snapshot();
let mut hunk_edits: Vec<Edit<Point>> = Vec::new();
@@ -536,31 +489,55 @@ impl BufferDiffSnapshot {
}
let hunk_patch = Patch::new(hunk_edits);
- let composed = hunk_patch.compose(buffer.edits_since::<Point>(original_snapshot.version()));
-
- let mut points = points.into_iter().peekable();
+ hunk_patch.compose(buffer.edits_since::<Point>(original_snapshot.version()))
+ }
- let first_group = points.peek().map(|point| {
- let (_, result) = translate_point_through_patch(&composed, *point);
- result
- });
+ pub fn buffer_point_to_base_text_range(
+ &self,
+ point: Point,
+ buffer: &text::BufferSnapshot,
+ ) -> Range<Point> {
+ let patch = self.patch_for_buffer_range(point..=point, buffer);
+ let edit = patch.edit_for_old_position(point);
+ edit.new
+ }
- let prev_boundary = points.peek().and_then(|first_point| {
- if first_point.row > 0 {
- let prev_point = Point::new(first_point.row - 1, 0);
- let (range, _) = translate_point_through_patch(&composed, prev_point);
- Some((prev_point, range))
- } else {
- None
- }
- });
+ pub fn base_text_point_to_buffer_range(
+ &self,
+ point: Point,
+ buffer: &text::BufferSnapshot,
+ ) -> Range<Point> {
+ let patch = self.patch_for_base_text_range(point..=point, buffer);
+ let edit = patch.edit_for_old_position(point);
+ edit.new
+ }
- let iter = points.map(move |point| {
- let (range, _) = translate_point_through_patch(&composed, point);
- range
- });
+ pub fn buffer_point_to_base_text_point(
+ &self,
+ point: Point,
+ buffer: &text::BufferSnapshot,
+ ) -> Point {
+ let patch = self.patch_for_buffer_range(point..=point, buffer);
+ let edit = patch.edit_for_old_position(point);
+ if point == edit.old.end {
+ edit.new.end
+ } else {
+ edit.new.start
+ }
+ }
- (iter, first_group, prev_boundary)
+ pub fn base_text_point_to_buffer_point(
+ &self,
+ point: Point,
+ buffer: &text::BufferSnapshot,
+ ) -> Point {
+ let patch = self.patch_for_base_text_range(point..=point, buffer);
+ let edit = patch.edit_for_old_position(point);
+ if point == edit.old.end {
+ edit.new.end
+ } else {
+ edit.new.start
+ }
}
}
@@ -3190,1291 +3167,4 @@ mod tests {
"extended_range should equal changed_range when edit is within the hunk"
);
}
-
- fn assert_rows_to_base_text_rows_visual(
- buffer: &Entity<language::Buffer>,
- diff: &Entity<BufferDiff>,
- source_text: &str,
- annotated_target: &str,
- cx: &mut gpui::TestAppContext,
- ) {
- let (target_text, expected_ranges) = parse_row_annotations(annotated_target);
-
- let buffer = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
- let diff = diff.update(cx, |diff, cx| diff.snapshot(cx));
-
- assert_eq!(
- buffer.text(),
- source_text,
- "buffer text does not match source text"
- );
-
- assert_eq!(
- diff.base_text_string().unwrap_or_default(),
- target_text,
- "base text does not match stripped annotated target"
- );
-
- let num_rows = source_text.lines().count() as u32;
- let max_point = buffer.max_point();
- let points = (0..=num_rows).map(move |row| {
- if row == num_rows && max_point.column > 0 {
- max_point
- } else {
- Point::new(row, 0)
- }
- });
- let actual_ranges: Vec<_> = diff.points_to_base_text_points(points, &buffer).0.collect();
-
- assert_eq!(
- actual_ranges, expected_ranges,
- "\nsource (buffer):\n{}\ntarget (base):\n{}\nexpected: {:?}\nactual: {:?}",
- source_text, target_text, expected_ranges, actual_ranges
- );
- }
-
- fn assert_base_text_rows_to_rows_visual(
- buffer: &Entity<language::Buffer>,
- diff: &Entity<BufferDiff>,
- source_text: &str,
- annotated_target: &str,
- cx: &mut gpui::TestAppContext,
- ) {
- let (target_text, expected_ranges) = parse_row_annotations(annotated_target);
-
- let buffer = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
- let diff = diff.update(cx, |diff, cx| diff.snapshot(cx));
-
- assert_eq!(
- diff.base_text_string().unwrap_or_default(),
- source_text,
- "base text does not match source text"
- );
-
- assert_eq!(
- buffer.text(),
- target_text,
- "buffer text does not match stripped annotated target"
- );
-
- let num_rows = source_text.lines().count() as u32;
- let base_max_point = diff.base_text().max_point();
- let points = (0..=num_rows).map(move |row| {
- if row == num_rows && base_max_point.column > 0 {
- base_max_point
- } else {
- Point::new(row, 0)
- }
- });
- let actual_ranges: Vec<_> = diff.base_text_points_to_points(points, &buffer).0.collect();
-
- assert_eq!(
- actual_ranges, expected_ranges,
- "\nsource (base):\n{}\ntarget (buffer):\n{}\nexpected: {:?}\nactual: {:?}",
- source_text, target_text, expected_ranges, actual_ranges
- );
- }
-
- fn parse_row_annotations(annotated_text: &str) -> (String, Vec<Range<Point>>) {
- let mut starts: std::collections::HashMap<u32, Point> = std::collections::HashMap::new();
- let mut ends: std::collections::HashMap<u32, Point> = std::collections::HashMap::new();
-
- let mut clean_text = String::new();
- let mut current_point = Point::new(0, 0);
- let mut chars = annotated_text.chars().peekable();
-
- while let Some(c) = chars.next() {
- if c == '<' {
- let mut num_str = String::new();
- while let Some(&next) = chars.peek() {
- if next.is_ascii_digit() {
- num_str.push(chars.next().unwrap());
- } else {
- break;
- }
- }
- if !num_str.is_empty() {
- let row_num: u32 = num_str.parse().unwrap();
- starts.insert(row_num, current_point);
-
- if chars.peek() == Some(&'>') {
- chars.next();
- ends.insert(row_num, current_point);
- }
- } else {
- clean_text.push(c);
- current_point.column += 1;
- }
- } else if c.is_ascii_digit() {
- let mut num_str = String::from(c);
- while let Some(&next) = chars.peek() {
- if next.is_ascii_digit() {
- num_str.push(chars.next().unwrap());
- } else {
- break;
- }
- }
- if chars.peek() == Some(&'>') {
- chars.next();
- let row_num: u32 = num_str.parse().unwrap();
- ends.insert(row_num, current_point);
- } else {
- for ch in num_str.chars() {
- clean_text.push(ch);
- current_point.column += 1;
- }
- }
- } else if c == '\n' {
- clean_text.push(c);
- current_point.row += 1;
- current_point.column = 0;
- } else {
- clean_text.push(c);
- current_point.column += 1;
- }
- }
-
- let max_row = starts.keys().chain(ends.keys()).max().copied().unwrap_or(0);
- let mut ranges: Vec<Range<Point>> = Vec::new();
- for row in 0..=max_row {
- let start = starts.get(&row).copied().unwrap_or(Point::new(0, 0));
- let end = ends.get(&row).copied().unwrap_or(start);
- ranges.push(start..end);
- }
-
- (clean_text, ranges)
- }
-
- fn make_diff(
- base_text: &str,
- buffer_text: &str,
- cx: &mut gpui::TestAppContext,
- ) -> (Entity<language::Buffer>, Entity<BufferDiff>) {
- let buffer = cx.new(|cx| language::Buffer::local(buffer_text, cx));
- let diff = cx.new(|cx| {
- BufferDiff::new_with_base_text(base_text, &buffer.read(cx).text_snapshot(), cx)
- });
- (buffer, diff)
- }
-
- #[gpui::test]
- async fn test_row_translation_visual(cx: &mut gpui::TestAppContext) {
- use unindent::Unindent;
-
- {
- let buffer_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1>bbb
- <2>ccc
- <3>"
- .unindent();
- let (base_text, _) = parse_row_annotations(&annotated_base);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let annotated_buffer = "
- <0>aaa
- <1>bbb
- <2>ccc
- <3>"
- .unindent();
- let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let buffer_text = "
- XXX
- bbb
- ccc
- "
- .unindent();
- let annotated_base = "
- <0<1aaa
- 0>1>bbb
- <2>ccc
- <3>"
- .unindent();
- let (base_text, _) = parse_row_annotations(&annotated_base);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
- }
-
- {
- let buffer_text = "
- aaa
- NEW
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1><2>ccc
- <3>"
- .unindent();
- let (base_text, _) = parse_row_annotations(&annotated_base);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
- }
-
- {
- let base_text = "
- aaa
- ccc
- "
- .unindent();
- let annotated_buffer = "
- <0>aaa
- <1NEW
- 1>ccc
- <2>"
- .unindent();
- let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let buffer_text = "aaa\nbbb";
- let annotated_base = "<0>aaa\n<1>bbb<2>";
- let (base_text, _) = parse_row_annotations(annotated_base);
- let (buffer, diff) = make_diff(&base_text, buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, buffer_text, annotated_base, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, annotated_base, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let annotated_buffer = "
- <0<1XXX
- 0>1>bbb
- <2>ccc
- <3>"
- .unindent();
- let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let buffer_text = "
- aaa
- bbb
- XXX
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1>bbb
- <2<3ccc
- 2>3>"
- .unindent();
- let (base_text, _) = parse_row_annotations(&annotated_base);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let annotated_buffer = "
- <0>aaa
- <1>bbb
- <2<3XXX
- 2>3>"
- .unindent();
- let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let buffer_text = "
- aaa
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1DELETED
- 1>ccc
- <2>"
- .unindent();
- let (base_text, _) = parse_row_annotations(&annotated_base);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
- }
-
- {
- let base_text = "
- aaa
- DELETED
- ccc
- "
- .unindent();
- let annotated_buffer = "
- <0>aaa
- <1><2>ccc
- <3>"
- .unindent();
- let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
- }
-
- #[gpui::test]
- async fn test_row_translation_with_edits_since_diff(cx: &mut gpui::TestAppContext) {
- use unindent::Unindent;
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..7, "XXX")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- XXX
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1bbb1>
- <2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..7, "XXX")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1XXX1>
- <2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "NEW\n")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- NEW
- bbb
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1><2>bbb
- <3>ccc
- <4>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "NEW\n")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1NEW
- 1>bbb
- <2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..8, "")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1bbb
- 1>ccc
- <2>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..8, "")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1><2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- ddd
- eee
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- ddd
- eee
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(12..15, "YYY")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- XXX
- ccc
- YYY
- eee
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3ddd3>
- <4>eee
- <5>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- ddd
- eee
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- ddd
- eee
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(12..15, "YYY")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1<2XXX
- 1>2>ccc
- <3YYY3>
- <4>eee
- <5>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(0..0, "NEW\n")], None, cx);
- });
-
- let new_buffer_text = "
- NEW
- aaa
- bbb
- ccc
- "
- .unindent();
- let annotated_base = "
- <0><1>aaa
- <2>bbb
- <3>ccc
- <4>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(0..0, "NEW\n")], None, cx);
- });
-
- let annotated_buffer = "
- <0NEW
- 0>aaa
- <1>bbb
- <2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(12..12, "NEW\n")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- bbb
- ccc
- NEW
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1>bbb
- <2>ccc
- <3><4>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = base_text.clone();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(12..12, "NEW\n")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1>bbb
- <2>ccc
- <3NEW
- 3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "";
- let buffer_text = "aaa\n";
- let (buffer, diff) = make_diff(base_text, buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "bbb\n")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- bbb
- "
- .unindent();
- let annotated_base = "<0><1><2>";
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "aaa\n";
- let buffer_text = "";
- let (buffer, diff) = make_diff(base_text, buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(0..0, "bbb\n")], None, cx);
- });
-
- let new_buffer_text = "bbb\n";
- let annotated_base = "
- <0<1aaa
- 0>1>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "";
- let buffer_text = "";
- let (buffer, diff) = make_diff(base_text, buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(0..0, "aaa\n")], None, cx);
- });
-
- let new_buffer_text = "aaa\n";
- let annotated_base = "<0><1>";
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..7, "YYY")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- YYY
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..7, "YYY")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1<2YYY
- 1>2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..6, "YY")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- YYXX
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(6..8, "YY")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- XXYY
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "NEW")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- NEWXXX
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(7..7, "NEW")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- XXXNEW
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "NEW\n")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- NEW
- ccc
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2bbb
- 1>2>ccc
- <3>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "NEW\n")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1<2NEW
- 1>2>ccc
- <3>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- ddd
- "
- .unindent();
- let buffer_text = "
- aaa
- ddd
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
- });
-
- let new_buffer_text = "
- aaa
- XXX
- YYY
- ddd
- "
- .unindent();
- let annotated_base = "
- <0>aaa
- <1<2<3bbb
- ccc
- 1>2>3>ddd
- <4>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- ddd
- "
- .unindent();
- let buffer_text = "
- aaa
- ddd
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
- });
-
- let annotated_buffer = "
- <0>aaa
- <1<2<3XXX
- YYY
- 1>2>3>ddd
- <4>"
- .unindent();
- assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(2..10, "YY\nZZ")], None, cx);
- });
-
- let new_buffer_text = "
- aaYY
- ZZcc
- "
- .unindent();
- let annotated_base = "
- <0>aa<1a
- bbb
- c1>cc
- <2>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
-
- {
- let base_text = "
- aaa
- bbb
- ccc
- "
- .unindent();
- let buffer_text = "
- aaa
- XXXX
- ccc
- "
- .unindent();
- let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(0..9, "ZZ\n")], None, cx);
- });
-
- let new_buffer_text = "
- ZZ
- ccc
- "
- .unindent();
- let annotated_base = "
- <0<1aaa
- bbb
- 0>1>ccc
- <2>"
- .unindent();
- assert_rows_to_base_text_rows_visual(
- &buffer,
- &diff,
- &new_buffer_text,
- &annotated_base,
- cx,
- );
- }
- }
-
- #[gpui::test]
- async fn test_row_translation_no_base_text(cx: &mut gpui::TestAppContext) {
- let buffer_text = "aaa\nbbb\nccc\n";
- let buffer = cx.new(|cx| language::Buffer::local(buffer_text, cx));
- let diff = cx.new(|cx| BufferDiff::new(&buffer.read(cx).text_snapshot(), cx));
-
- let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
- let diff_snapshot = diff.update(cx, |diff, cx| diff.snapshot(cx));
-
- let points = vec![
- Point::new(0, 0),
- Point::new(1, 0),
- Point::new(2, 0),
- Point::new(3, 0),
- ];
- let base_rows: Vec<_> = diff_snapshot
- .points_to_base_text_points(points, &buffer_snapshot)
- .0
- .collect();
-
- let zero = Point::new(0, 0);
- assert_eq!(
- base_rows,
- vec![zero..zero, zero..zero, zero..zero, zero..zero],
- "all buffer rows should map to point 0,0 in empty base text"
- );
-
- let base_points = vec![Point::new(0, 0)];
- let (rows_iter, _, _) =
- diff_snapshot.base_text_points_to_points(base_points, &buffer_snapshot);
- let buffer_rows: Vec<_> = rows_iter.collect();
-
- let max_point = buffer_snapshot.max_point();
- assert_eq!(
- buffer_rows,
- vec![zero..max_point],
- "base text row 0 should map to entire buffer range"
- );
- }
}
@@ -1,4 +1,4 @@
-use std::ops::{Bound, Range};
+use std::ops::{Bound, Range, RangeInclusive};
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
use collections::HashMap;
@@ -6,19 +6,19 @@ use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
use gpui::{Action, AppContext as _, Entity, EventEmitter, Focusable, Subscription, WeakEntity};
use language::{Buffer, Capability};
use multi_buffer::{
- Anchor, BufferOffset, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer,
- MultiBufferPoint, MultiBufferSnapshot, PathKey,
+ Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, MultiBufferPoint,
+ MultiBufferSnapshot, PathKey,
};
use project::Project;
use rope::Point;
-use text::{OffsetRangeExt as _, ToPoint as _};
+use text::{OffsetRangeExt as _, Patch, ToPoint as _};
use ui::{
App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render,
Styled as _, Window, div,
};
use crate::{
- display_map::MultiBufferRowMapping,
+ display_map::CompanionExcerptPatch,
split_editor_view::{SplitEditorState, SplitEditorView},
};
use workspace::{ActivatePaneLeft, ActivatePaneRight, Item, Workspace};
@@ -35,17 +35,13 @@ pub(crate) fn convert_lhs_rows_to_rhs(
rhs_snapshot: &MultiBufferSnapshot,
lhs_snapshot: &MultiBufferSnapshot,
lhs_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
-) -> Vec<MultiBufferRowMapping> {
- convert_rows(
+) -> Vec<CompanionExcerptPatch> {
+ patches_for_range(
lhs_excerpt_to_rhs_excerpt,
lhs_snapshot,
rhs_snapshot,
lhs_bounds,
- |diff, points, buffer| {
- let (points, first_group, prev_boundary) =
- diff.base_text_points_to_points(points, buffer);
- (points.collect(), first_group, prev_boundary)
- },
+ |diff, range, buffer| diff.patch_for_base_text_range(range, buffer),
)
}
@@ -54,182 +50,171 @@ pub(crate) fn convert_rhs_rows_to_lhs(
lhs_snapshot: &MultiBufferSnapshot,
rhs_snapshot: &MultiBufferSnapshot,
rhs_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
-) -> Vec<MultiBufferRowMapping> {
- convert_rows(
+) -> Vec<CompanionExcerptPatch> {
+ patches_for_range(
rhs_excerpt_to_lhs_excerpt,
rhs_snapshot,
lhs_snapshot,
rhs_bounds,
- |diff, points, buffer| {
- let (points, first_group, prev_boundary) =
- diff.points_to_base_text_points(points, buffer);
- (points.collect(), first_group, prev_boundary)
- },
+ |diff, range, buffer| diff.patch_for_buffer_range(range, buffer),
)
}
-fn convert_rows<F>(
+fn patches_for_range<F>(
excerpt_map: &HashMap<ExcerptId, ExcerptId>,
source_snapshot: &MultiBufferSnapshot,
target_snapshot: &MultiBufferSnapshot,
source_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
translate_fn: F,
-) -> Vec<MultiBufferRowMapping>
+) -> Vec<CompanionExcerptPatch>
where
- F: Fn(
- &BufferDiffSnapshot,
- Vec<Point>,
- &text::BufferSnapshot,
- ) -> (
- Vec<Range<Point>>,
- Option<Range<Point>>,
- Option<(Point, Range<Point>)>,
- ),
+ F: Fn(&BufferDiffSnapshot, RangeInclusive<Point>, &text::BufferSnapshot) -> Patch<Point>,
{
let mut result = Vec::new();
+ let mut patches = HashMap::default();
- for (buffer, buffer_offset_range, source_excerpt_id) in
+ for (source_buffer, buffer_offset_range, source_excerpt_id) in
source_snapshot.range_to_buffer_ranges(source_bounds)
{
- if let Some(translation) = convert_excerpt_rows(
- excerpt_map,
+ let target_excerpt_id = excerpt_map.get(&source_excerpt_id).copied().unwrap();
+ let target_buffer = target_snapshot
+ .buffer_for_excerpt(target_excerpt_id)
+ .unwrap();
+ let patch = patches.entry(source_buffer.remote_id()).or_insert_with(|| {
+ let diff = source_snapshot
+ .diff_for_buffer_id(source_buffer.remote_id())
+ .unwrap();
+ let rhs_buffer = if source_buffer.remote_id() == diff.base_text().remote_id() {
+ &target_buffer
+ } else {
+ source_buffer
+ };
+ // TODO(split-diff) pass only the union of the ranges for the affected excerpts
+ translate_fn(diff, Point::zero()..=source_buffer.max_point(), rhs_buffer)
+ });
+ let buffer_point_range = buffer_offset_range.to_point(source_buffer);
+
+ // TODO(split-diff) maybe narrow the patch to only the edited part of the excerpt
+ // (less useful for project diff, but important if we want to do singleton side-by-side diff)
+ result.push(patch_for_excerpt(
source_snapshot,
target_snapshot,
source_excerpt_id,
- buffer,
- buffer_offset_range,
- &translate_fn,
- ) {
- result.push(translation);
- }
+ target_excerpt_id,
+ source_buffer,
+ target_buffer,
+ patch,
+ buffer_point_range,
+ ));
}
result
}
-fn convert_excerpt_rows<F>(
- excerpt_map: &HashMap<ExcerptId, ExcerptId>,
+fn patch_for_excerpt(
source_snapshot: &MultiBufferSnapshot,
target_snapshot: &MultiBufferSnapshot,
source_excerpt_id: ExcerptId,
+ target_excerpt_id: ExcerptId,
source_buffer: &text::BufferSnapshot,
- source_buffer_range: Range<BufferOffset>,
- translate_fn: F,
-) -> Option<MultiBufferRowMapping>
-where
- F: Fn(
- &BufferDiffSnapshot,
- Vec<Point>,
- &text::BufferSnapshot,
- ) -> (
- Vec<Range<Point>>,
- Option<Range<Point>>,
- Option<(Point, Range<Point>)>,
- ),
-{
- let target_excerpt_id = excerpt_map.get(&source_excerpt_id).copied()?;
- let target_buffer = target_snapshot.buffer_for_excerpt(target_excerpt_id)?;
-
- let diff = source_snapshot.diff_for_buffer_id(source_buffer.remote_id())?;
- let rhs_buffer = if source_buffer.remote_id() == diff.base_text().remote_id() {
- &target_buffer
- } else {
- source_buffer
- };
-
- let local_start = source_buffer.offset_to_point(source_buffer_range.start.0);
- let local_end = source_buffer.offset_to_point(source_buffer_range.end.0);
-
- let mut input_points: Vec<Point> = (local_start.row..=local_end.row)
- .map(|row| Point::new(row, 0))
- .collect();
- if local_end.column > 0 {
- input_points.push(local_end);
- }
-
- let (translated_ranges, first_group, prev_boundary) =
- translate_fn(&diff, input_points.clone(), rhs_buffer);
-
- let source_multibuffer_range = source_snapshot.range_for_excerpt(source_excerpt_id)?;
+ target_buffer: &text::BufferSnapshot,
+ patch: &Patch<Point>,
+ source_edited_range: Range<Point>,
+) -> CompanionExcerptPatch {
+ let source_multibuffer_range = source_snapshot
+ .range_for_excerpt(source_excerpt_id)
+ .unwrap();
let source_excerpt_start_in_multibuffer = source_multibuffer_range.start;
- let source_context_range = source_snapshot.context_range_for_excerpt(source_excerpt_id)?;
+ let source_context_range = source_snapshot
+ .context_range_for_excerpt(source_excerpt_id)
+ .unwrap();
let source_excerpt_start_in_buffer = source_context_range.start.to_point(&source_buffer);
let source_excerpt_end_in_buffer = source_context_range.end.to_point(&source_buffer);
- let target_multibuffer_range = target_snapshot.range_for_excerpt(target_excerpt_id)?;
+ let target_multibuffer_range = target_snapshot
+ .range_for_excerpt(target_excerpt_id)
+ .unwrap();
let target_excerpt_start_in_multibuffer = target_multibuffer_range.start;
- let target_context_range = target_snapshot.context_range_for_excerpt(target_excerpt_id)?;
+ let target_context_range = target_snapshot
+ .context_range_for_excerpt(target_excerpt_id)
+ .unwrap();
let target_excerpt_start_in_buffer = target_context_range.start.to_point(&target_buffer);
let target_excerpt_end_in_buffer = target_context_range.end.to_point(&target_buffer);
- let boundaries: Vec<_> = input_points
- .into_iter()
- .zip(translated_ranges)
- .map(|(source_buffer_point, target_range)| {
- let source_multibuffer_point = source_excerpt_start_in_multibuffer
- + (source_buffer_point - source_excerpt_start_in_buffer.min(source_buffer_point));
-
- let clamped_target_start = target_range
+ let edits = patch
+ .edits()
+ .iter()
+ .skip_while(|edit| edit.old.end < source_excerpt_start_in_buffer)
+ .take_while(|edit| edit.old.start <= source_excerpt_end_in_buffer)
+ .map(|edit| {
+ let clamped_source_start = edit
+ .old
+ .start
+ .max(source_excerpt_start_in_buffer)
+ .min(source_excerpt_end_in_buffer);
+ let clamped_source_end = edit
+ .old
+ .end
+ .max(source_excerpt_start_in_buffer)
+ .min(source_excerpt_end_in_buffer);
+ let source_multibuffer_start = source_excerpt_start_in_multibuffer
+ + (clamped_source_start - source_excerpt_start_in_buffer);
+ let source_multibuffer_end = source_excerpt_start_in_multibuffer
+ + (clamped_source_end - source_excerpt_start_in_buffer);
+ let clamped_target_start = edit
+ .new
.start
.max(target_excerpt_start_in_buffer)
.min(target_excerpt_end_in_buffer);
- let clamped_target_end = target_range
+ let clamped_target_end = edit
+ .new
.end
.max(target_excerpt_start_in_buffer)
.min(target_excerpt_end_in_buffer);
-
let target_multibuffer_start = target_excerpt_start_in_multibuffer
+ (clamped_target_start - target_excerpt_start_in_buffer);
-
let target_multibuffer_end = target_excerpt_start_in_multibuffer
+ (clamped_target_end - target_excerpt_start_in_buffer);
+ text::Edit {
+ old: source_multibuffer_start..source_multibuffer_end,
+ new: target_multibuffer_start..target_multibuffer_end,
+ }
+ });
+
+ let edits = [text::Edit {
+ old: source_excerpt_start_in_multibuffer..source_excerpt_start_in_multibuffer,
+ new: target_excerpt_start_in_multibuffer..target_excerpt_start_in_multibuffer,
+ }]
+ .into_iter()
+ .chain(edits);
- (
- source_multibuffer_point,
- target_multibuffer_start..target_multibuffer_end,
- )
- })
- .collect();
- let first_group = first_group.map(|first_group| {
- let start = source_excerpt_start_in_multibuffer
- + (first_group.start - source_excerpt_start_in_buffer.min(first_group.start));
- let end = source_excerpt_start_in_multibuffer
- + (first_group.end - source_excerpt_start_in_buffer.min(first_group.end));
- start..end
- });
-
- let prev_boundary = prev_boundary.map(|(source_buffer_point, target_range)| {
- let source_multibuffer_point = source_excerpt_start_in_multibuffer
- + (source_buffer_point - source_excerpt_start_in_buffer.min(source_buffer_point));
-
- let clamped_target_start = target_range
- .start
- .max(target_excerpt_start_in_buffer)
- .min(target_excerpt_end_in_buffer);
- let clamped_target_end = target_range
- .end
- .max(target_excerpt_start_in_buffer)
- .min(target_excerpt_end_in_buffer);
-
- let target_multibuffer_start = target_excerpt_start_in_multibuffer
- + (clamped_target_start - target_excerpt_start_in_buffer);
- let target_multibuffer_end = target_excerpt_start_in_multibuffer
- + (clamped_target_end - target_excerpt_start_in_buffer);
-
- (
- source_multibuffer_point,
- target_multibuffer_start..target_multibuffer_end,
- )
- });
-
- Some(MultiBufferRowMapping {
- boundaries,
- first_group,
- prev_boundary,
- source_excerpt_end: source_excerpt_start_in_multibuffer
- + (source_excerpt_end_in_buffer - source_excerpt_start_in_buffer),
- target_excerpt_end: target_excerpt_start_in_multibuffer
- + (target_excerpt_end_in_buffer - target_excerpt_start_in_buffer),
- })
+ let mut merged_edits: Vec<text::Edit<Point>> = Vec::new();
+ for edit in edits {
+ if let Some(last) = merged_edits.last_mut() {
+ if edit.new.start <= last.new.end {
+ last.old.end = last.old.end.max(edit.old.end);
+ last.new.end = last.new.end.max(edit.new.end);
+ continue;
+ }
+ }
+ merged_edits.push(edit);
+ }
+
+ let edited_range = source_excerpt_start_in_multibuffer
+ + (source_edited_range.start - source_excerpt_start_in_buffer)
+ ..source_excerpt_start_in_multibuffer
+ + (source_edited_range.end - source_excerpt_start_in_buffer);
+
+ let source_excerpt_end = source_excerpt_start_in_multibuffer
+ + (source_excerpt_end_in_buffer - source_excerpt_start_in_buffer);
+ let target_excerpt_end = target_excerpt_start_in_multibuffer
+ + (target_excerpt_end_in_buffer - target_excerpt_start_in_buffer);
+
+ CompanionExcerptPatch {
+ patch: Patch::new(merged_edits),
+ edited_range,
+ source_excerpt_range: source_excerpt_start_in_multibuffer..source_excerpt_end,
+ target_excerpt_range: target_excerpt_start_in_multibuffer..target_excerpt_end,
+ }
}
pub struct SplitDiffFeatureFlag;
@@ -623,24 +608,16 @@ impl SplittableEditor {
let source_snapshot = source_multibuffer.read(cx).snapshot(cx);
let target_snapshot = target_multibuffer.read(cx).snapshot(cx);
- let target_point = target_editor.update(cx, |target_editor, cx| {
+ let target_range = target_editor.update(cx, |target_editor, cx| {
target_editor.display_map.update(cx, |display_map, cx| {
let display_map_id = cx.entity_id();
display_map.companion().unwrap().update(cx, |companion, _| {
- companion
- .convert_rows_from_companion(
- display_map_id,
- &target_snapshot,
- &source_snapshot,
- (Bound::Included(source_point), Bound::Included(source_point)),
- )
- .first()
- .unwrap()
- .boundaries
- .first()
- .unwrap()
- .1
- .start
+ companion.convert_point_from_companion(
+ display_map_id,
+ &target_snapshot,
+ &source_snapshot,
+ source_point,
+ )
})
})
});
@@ -648,7 +625,7 @@ impl SplittableEditor {
target_editor.update(cx, |editor, cx| {
editor.set_suppress_selection_callback(true);
editor.change_selections(crate::SelectionEffects::no_scroll(), window, cx, |s| {
- s.select_ranges([target_point..target_point]);
+ s.select_ranges([target_range]);
});
editor.set_suppress_selection_callback(false);
});
@@ -1501,14 +1478,17 @@ impl LhsEditor {
.into_iter()
.map(|(_, excerpt_range)| {
let point_range_to_base_text_point_range = |range: Range<Point>| {
- let (mut translated, _, _) = diff_snapshot.points_to_base_text_points(
- [Point::new(range.start.row, 0), Point::new(range.end.row, 0)],
- main_buffer,
- );
- let start_row = translated.next().unwrap().start.row;
- let end_row = translated.next().unwrap().end.row;
- let end_column = diff_snapshot.base_text().line_len(end_row);
- Point::new(start_row, 0)..Point::new(end_row, end_column)
+ let start = diff_snapshot
+ .buffer_point_to_base_text_range(
+ Point::new(range.start.row, 0),
+ main_buffer,
+ )
+ .start;
+ let end = diff_snapshot
+ .buffer_point_to_base_text_range(Point::new(range.end.row, 0), main_buffer)
+ .end;
+ let end_column = diff_snapshot.base_text().line_len(end.row);
+ Point::new(start.row, 0)..Point::new(end.row, end_column)
};
let rhs = excerpt_range.primary.to_point(main_buffer);
let context = excerpt_range.context.to_point(main_buffer);