History

Merge remote-tracking branch 'origin/main' into modeline

Marc-André Lureau committed 9 months ago

Commit d2fdc1d2 parents bf0ae28 27ab8981109 files · +120203 −56017Browse files

Showing the changes to crates/buffer_diff/src/buffer_diff.rs (1 of 1109 files). Show all

Unified · Split

crates/buffer_diff/src/buffer_diff.rs+894−1423
@@ -6,42 +6,19 @@ use language::{ · 5 unmodified lines
66 language_settings::LanguageSettings, word_diff_ranges,
77 };
88 use rope::Rope;
9−use std::{cmp::Ordering, future::Future, iter, ops::Range, sync::Arc};
9+use std::{
10+ cmp::Ordering,
11+ future::Future,
12+ iter,
13+ ops::{Range, RangeInclusive},
14+ sync::Arc,
15+};
1016 use sum_tree::SumTree;
1117 use text::{
1218 Anchor, Bias, BufferId, Edit, OffsetRangeExt, Patch, Point, ToOffset as _, ToPoint as _,
1319 };
1420 use util::ResultExt;
1521
16−fn translate_point_through_patch(
17− patch: &Patch<Point>,
18− point: Point,
19−) -> (Range<Point>, Range<Point>) {
20− let edits = patch.edits();
21−
22− let ix = match edits.binary_search_by(|probe| probe.old.start.cmp(&point)) {
23− Ok(ix) => ix,
24− Err(ix) => {
25− if ix == 0 {
26− return (point..point, point..point);
27− } else {
28− ix - 1
29− }
30− }
31− };
32−
33− if let Some(edit) = edits.get(ix) {
34− if point > edit.old.end {
35− let translated = edit.new.end + (point - edit.old.end);
36− (translated..translated, point..point)
37− } else {
38− (edit.new.start..edit.new.end, edit.old.start..edit.old.end)
39− }
40− } else {
41− (point..point, point..point)
42− }
43−}
44−
4522 pub const MAX_WORD_DIFF_LINE_COUNT: usize = 5;
4623
4724 pub struct BufferDiff {
@@ -53,7 +30,7 @@ pub struct BufferDiff { · 5 unmodified lines
5330 #[derive(Clone)]
5431 pub struct BufferDiffSnapshot {
5532 inner: BufferDiffInner<language::BufferSnapshot>,
56− secondary_diff: Option<Box<BufferDiffSnapshot>>,
33+ secondary_diff: Option<Arc<BufferDiffSnapshot>>,
5734 }
5835
5936 impl std::fmt::Debug for BufferDiffSnapshot {
@@ -245,11 +222,11 @@ impl std::fmt::Debug for BufferDiffInner<language::BufferSnapshot> { · 185 unmodified lines
245222 impl BufferDiffSnapshot {
246223 #[cfg(test)]
247224 fn new_sync(
248− buffer: text::BufferSnapshot,
225+ buffer: &text::BufferSnapshot,
249226 diff_base: String,
250227 cx: &mut gpui::TestAppContext,
251228 ) -> BufferDiffSnapshot {
252− let buffer_diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
229+ let buffer_diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, buffer, cx));
253230 buffer_diff.update(cx, |buffer_diff, cx| buffer_diff.snapshot(cx))
254231 }
255232
@@ -385,7 +362,7 @@ impl BufferDiffSnapshot { · 129 unmodified lines
385362 (new_id == old_id && new_version == old_version) || (new_empty && old_empty)
386363 }
387364
388− #[allow(unused)]
365+ /// Returns the last hunk whose start is less than or equal to the given position.
389366 fn hunk_before_base_text_offset<'a>(
390367 &self,
391368 target: usize,
@@ -398,16 +375,11 @@ impl BufferDiffSnapshot { · 6 unmodified lines
398375 {
399376 cursor.prev();
400377 }
401− let result = cursor
378+ cursor
402379 .item()
403− .filter(|hunk| target >= hunk.diff_base_byte_range.start);
404− if cursor.item().is_none() {
405− cursor.reset();
406− }
407− result
380+ .filter(|hunk| target >= hunk.diff_base_byte_range.start)
408381 }
409382
410− #[allow(unused)]
411383 fn hunk_before_buffer_anchor<'a>(
412384 &self,
413385 target: Anchor,
@@ -421,24 +393,92 @@ impl BufferDiffSnapshot { · 7 unmodified lines
421393 {
422394 cursor.prev();
423395 }
424− let result = cursor
396+ cursor
425397 .item()
426− .filter(|hunk| target.cmp(&hunk.buffer_range.start, buffer).is_ge());
398+ .filter(|hunk| target.cmp(&hunk.buffer_range.start, buffer).is_ge())
399+ }
400+
401+ /// Returns a patch mapping the provided main buffer snapshot to the base text of this diff.
402+ ///
403+ /// The returned patch is guaranteed to be accurate for all main buffer points in the provided range,
404+ /// but not necessarily for points outside that range.
405+ pub fn patch_for_buffer_range<'a>(
406+ &'a self,
407+ range: RangeInclusive<Point>,
408+ buffer: &'a text::BufferSnapshot,
409+ ) -> Patch<Point> {
410+ if !self.inner.base_text_exists {
411+ return Patch::new(vec![Edit {
412+ old: Point::zero()..buffer.max_point(),
413+ new: Point::zero()..Point::zero(),
414+ }]);
415+ }
416+
417+ let mut edits_since_diff = Patch::new(
418+ buffer
419+ .edits_since::<Point>(&self.inner.buffer_snapshot.version)
420+ .collect::<Vec<_>>(),
421+ );
422+ edits_since_diff.invert();
423+
424+ let mut start_point = edits_since_diff.old_to_new(*range.start());
425+ if let Some(first_edit) = edits_since_diff.edits().first() {
426+ start_point = start_point.min(first_edit.new.start);
427+ }
428+
429+ let original_snapshot = self.original_buffer_snapshot();
430+ let base_text = self.base_text();
431+
432+ let mut cursor = self.inner.hunks.cursor(original_snapshot);
433+ self.hunk_before_buffer_anchor(
434+ original_snapshot.anchor_before(start_point),
435+ &mut cursor,
436+ original_snapshot,
437+ );
427438 if cursor.item().is_none() {
428− cursor.reset();
439+ cursor.next();
440+ }
441+
442+ let mut prefix_edit = cursor.prev_item().map(|prev_hunk| Edit {
443+ old: Point::zero()..prev_hunk.buffer_range.end.to_point(original_snapshot),
444+ new: Point::zero()..prev_hunk.diff_base_byte_range.end.to_point(base_text),
445+ });
446+
447+ let mut range_end = edits_since_diff.old_to_new(*range.end());
448+ if let Some(last_edit) = edits_since_diff.edits().last() {
449+ range_end = range_end.max(last_edit.new.end);
429450 }
430− result
451+ let range_end = original_snapshot.anchor_before(range_end);
452+
453+ let hunk_iter = std::iter::from_fn(move || {
454+ if let Some(edit) = prefix_edit.take() {
455+ return Some(edit);
456+ }
457+ let hunk = cursor.item()?;
458+ if hunk
459+ .buffer_range
460+ .start
461+ .cmp(&range_end, original_snapshot)
462+ .is_gt()
463+ {
464+ return None;
465+ }
466+ let edit = Edit {
467+ old: hunk.buffer_range.to_point(original_snapshot),
468+ new: hunk.diff_base_byte_range.to_point(base_text),
469+ };
470+ cursor.next();
471+ Some(edit)
472+ });
473+
474+ edits_since_diff.compose(hunk_iter)
431475 }
432476
433− pub fn points_to_base_text_points<'a>(
477+ #[cfg(test)]
478+ pub(crate) fn patch_for_buffer_range_naive<'a>(
434479 &'a self,
435− points: impl IntoIterator<Item = Point> + 'a,
436480 buffer: &'a text::BufferSnapshot,
437− ) -> (
438− impl 'a + Iterator<Item = Range<Point>>,
439− Option<Range<Point>>,
440− Option<(Point, Range<Point>)>,
441− ) {
481+ ) -> Patch<Point> {
442482 let original_snapshot = self.original_buffer_snapshot();
443483
444484 let edits_since: Vec<Edit<Point>> = buffer
@@ -447,7 +487,7 @@ impl BufferDiffSnapshot { · 2 unmodified lines
447487 let mut inverted_edits_since = Patch::new(edits_since);
448488 inverted_edits_since.invert();
449489
450− let composed = inverted_edits_since.compose(
490+ inverted_edits_since.compose(
451491 self.inner
452492 .hunks
453493 .iter()
@@ -475,42 +515,92 @@ impl BufferDiffSnapshot { · 21 unmodified lines
475515 None
476516 },
477517 ),
478− );
479−
480− let mut points = points.into_iter().peekable();
518+ )
519+ }
481520
482− let first_group = points.peek().map(|point| {
483− let (_, old_range) = translate_point_through_patch(&composed, *point);
484− old_range
485− });
521+ /// Returns a patch mapping the base text of this diff to the provided main buffer snapshot.
522+ ///
523+ /// The returned patch is guaranteed to be accurate for all base text points in the provided range,
524+ /// but not necessarily for points outside that range.
525+ pub fn patch_for_base_text_range<'a>(
526+ &'a self,
527+ range: RangeInclusive<Point>,
528+ buffer: &'a text::BufferSnapshot,
529+ ) -> Patch<Point> {
530+ if !self.inner.base_text_exists {
531+ return Patch::new(vec![Edit {
532+ old: Point::zero()..Point::zero(),
533+ new: Point::zero()..buffer.max_point(),
534+ }]);
535+ }
486536
487− let prev_boundary = points.peek().and_then(|first_point| {
488− if first_point.row > 0 {
489− let prev_point = Point::new(first_point.row - 1, 0);
490− let (range, _) = translate_point_through_patch(&composed, prev_point);
491− Some((prev_point, range))
492− } else {
493− None
494− }
495− });
537+ let edits_since_diff = buffer
538+ .edits_since::<Point>(&self.inner.buffer_snapshot.version)
539+ .collect::<Vec<_>>();
496540
497− let iter = points.map(move |point| {
498− let (range, _) = translate_point_through_patch(&composed, point);
499− range
500− });
541+ let mut hunk_patch = Vec::new();
542+ let mut cursor = self.inner.hunks.cursor(self.original_buffer_snapshot());
543+ let hunk_before = self
544+ .hunk_before_base_text_offset(range.start().to_offset(self.base_text()), &mut cursor);
545+
546+ if let Some(hunk) = hunk_before
547+ && let Some(first_edit) = edits_since_diff.first()
548+ && hunk
549+ .buffer_range
550+ .start
551+ .to_point(self.original_buffer_snapshot())
552+ > first_edit.old.start
553+ {
554+ cursor.reset();
555+ self.hunk_before_buffer_anchor(
556+ self.original_buffer_snapshot()
557+ .anchor_before(first_edit.old.start),
558+ &mut cursor,
559+ self.original_buffer_snapshot(),
560+ );
561+ }
562+ if cursor.item().is_none() {
563+ cursor.next();
564+ }
565+ if let Some(prev_hunk) = cursor.prev_item() {
566+ hunk_patch.push(Edit {
567+ old: Point::zero()
568+ ..prev_hunk
569+ .diff_base_byte_range
570+ .end
571+ .to_point(self.base_text()),
572+ new: Point::zero()
573+ ..prev_hunk
574+ .buffer_range
575+ .end
576+ .to_point(self.original_buffer_snapshot()),
577+ })
578+ }
579+ let range_end = range.end().to_offset(self.base_text());
580+ while let Some(hunk) = cursor.item()
581+ && (hunk.diff_base_byte_range.start <= range_end
582+ || edits_since_diff.last().is_some_and(|last_edit| {
583+ hunk.buffer_range
584+ .start
585+ .to_point(self.original_buffer_snapshot())
586+ <= last_edit.old.end
587+ }))
588+ {
589+ hunk_patch.push(Edit {
590+ old: hunk.diff_base_byte_range.to_point(self.base_text()),
591+ new: hunk.buffer_range.to_point(self.original_buffer_snapshot()),
592+ });
593+ cursor.next();
594+ }
501595
502− (iter, first_group, prev_boundary)
596+ Patch::new(hunk_patch).compose(edits_since_diff)
503597 }
504598
505− pub fn base_text_points_to_points<'a>(
599+ #[cfg(test)]
600+ pub(crate) fn patch_for_base_text_range_naive<'a>(
506601 &'a self,
507− points: impl IntoIterator<Item = Point> + 'a,
508602 buffer: &'a text::BufferSnapshot,
509− ) -> (
510− impl 'a + Iterator<Item = Range<Point>>,
511− Option<Range<Point>>,
512− Option<(Point, Range<Point>)>,
513− ) {
603+ ) -> Patch<Point> {
514604 let original_snapshot = self.original_buffer_snapshot();
515605
516606 let mut hunk_edits: Vec<Edit<Point>> = Vec::new();
@@ -536,31 +626,55 @@ impl BufferDiffSnapshot { · 19 unmodified lines
536626 }
537627 let hunk_patch = Patch::new(hunk_edits);
538628
539− let composed = hunk_patch.compose(buffer.edits_since::<Point>(original_snapshot.version()));
540−
541− let mut points = points.into_iter().peekable();
629+ hunk_patch.compose(buffer.edits_since::<Point>(original_snapshot.version()))
630+ }
542631
543− let first_group = points.peek().map(|point| {
544− let (_, result) = translate_point_through_patch(&composed, *point);
545− result
546− });
632+ pub fn buffer_point_to_base_text_range(
633+ &self,
634+ point: Point,
635+ buffer: &text::BufferSnapshot,
636+ ) -> Range<Point> {
637+ let patch = self.patch_for_buffer_range(point..=point, buffer);
638+ let edit = patch.edit_for_old_position(point);
639+ edit.new
640+ }
547641
548− let prev_boundary = points.peek().and_then(|first_point| {
549− if first_point.row > 0 {
550− let prev_point = Point::new(first_point.row - 1, 0);
551− let (range, _) = translate_point_through_patch(&composed, prev_point);
552− Some((prev_point, range))
553− } else {
554− None
555− }
556− });
642+ pub fn base_text_point_to_buffer_range(
643+ &self,
644+ point: Point,
645+ buffer: &text::BufferSnapshot,
646+ ) -> Range<Point> {
647+ let patch = self.patch_for_base_text_range(point..=point, buffer);
648+ let edit = patch.edit_for_old_position(point);
649+ edit.new
650+ }
557651
558− let iter = points.map(move |point| {
559− let (range, _) = translate_point_through_patch(&composed, point);
560− range
561− });
652+ pub fn buffer_point_to_base_text_point(
653+ &self,
654+ point: Point,
655+ buffer: &text::BufferSnapshot,
656+ ) -> Point {
657+ let patch = self.patch_for_buffer_range(point..=point, buffer);
658+ let edit = patch.edit_for_old_position(point);
659+ if point == edit.old.end {
660+ edit.new.end
661+ } else {
662+ edit.new.start
663+ }
664+ }
562665
563− (iter, first_group, prev_boundary)
666+ pub fn base_text_point_to_buffer_point(
667+ &self,
668+ point: Point,
669+ buffer: &text::BufferSnapshot,
670+ ) -> Point {
671+ let patch = self.patch_for_base_text_range(point..=point, buffer);
672+ let edit = patch.edit_for_old_position(point);
673+ if point == edit.old.end {
674+ edit.new.end
675+ } else {
676+ edit.new.start
677+ }
564678 }
565679 }
566680
@@ -972,10 +1086,10 @@ fn build_diff_options( · 405 unmodified lines
9721086
9731087 fn compute_hunks(
9741088 diff_base: Option<(Arc<str>, Rope)>,
975− buffer: text::BufferSnapshot,
1089+ buffer: &text::BufferSnapshot,
9761090 diff_options: Option<DiffOptions>,
9771091 ) -> SumTree<InternalDiffHunk> {
978− let mut tree = SumTree::new(&buffer);
1092+ let mut tree = SumTree::new(buffer);
9791093
9801094 if let Some((diff_base, diff_base_rope)) = diff_base {
9811095 let buffer_text = buffer.as_rope().to_string();
@@ -1002,7 +1116,7 @@ fn compute_hunks( · 20 unmodified lines
10021116 base_word_diffs: Vec::default(),
10031117 buffer_word_diffs: Vec::default(),
10041118 },
1005− &buffer,
1119+ buffer,
10061120 );
10071121 return tree;
10081122 }
@@ -1014,11 +1128,11 @@ fn compute_hunks( · 5 unmodified lines
10141128 &patch,
10151129 hunk_index,
10161130 &diff_base_rope,
1017− &buffer,
1131+ buffer,
10181132 &mut divergence,
10191133 diff_options.as_ref(),
10201134 );
1021− tree.push(hunk, &buffer);
1135+ tree.push(hunk, buffer);
10221136 }
10231137 }
10241138 } else {
@@ -1029,7 +1143,7 @@ fn compute_hunks( · 4 unmodified lines
10291143 base_word_diffs: Vec::default(),
10301144 buffer_word_diffs: Vec::default(),
10311145 },
1032− &buffer,
1146+ buffer,
10331147 );
10341148 }
10351149
@@ -1041,6 +1155,8 @@ fn compare_hunks( · 5 unmodified lines
10411155 old_hunks: &SumTree<InternalDiffHunk>,
10421156 old_snapshot: &text::BufferSnapshot,
10431157 new_snapshot: &text::BufferSnapshot,
1158+ old_base_text: &text::BufferSnapshot,
1159+ new_base_text: &text::BufferSnapshot,
10441160 ) -> DiffChanged {
10451161 let mut new_cursor = new_hunks.cursor::<()>(new_snapshot);
10461162 let mut old_cursor = old_hunks.cursor::<()>(new_snapshot);
@@ -1048,8 +1164,8 @@ fn compare_hunks( · 1 unmodified line
10481164 new_cursor.next();
10491165 let mut start = None;
10501166 let mut end = None;
1051− let mut base_text_start = None;
1052− let mut base_text_end = None;
1167+ let mut base_text_start: Option<Anchor> = None;
1168+ let mut base_text_end: Option<Anchor> = None;
10531169
10541170 let mut last_unchanged_new_hunk_end: Option<text::Anchor> = None;
10551171 let mut has_changes = false;
@@ -1067,9 +1183,19 @@ fn compare_hunks( · 11 unmodified lines
10671183 has_changes = true;
10681184 extended_end_candidate = None;
10691185 start.get_or_insert(new_hunk.buffer_range.start);
1070− base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
1186+ base_text_start.get_or_insert(
1187+ new_base_text.anchor_before(new_hunk.diff_base_byte_range.start),
1188+ );
10711189 end.replace(new_hunk.buffer_range.end);
1072− base_text_end.replace(new_hunk.diff_base_byte_range.end);
1190+ let new_diff_range_end =
1191+ new_base_text.anchor_after(new_hunk.diff_base_byte_range.end);
1192+ if base_text_end.is_none_or(|base_text_end| {
1193+ new_diff_range_end
1194+ .cmp(&base_text_end, &new_base_text)
1195+ .is_gt()
1196+ }) {
1197+ base_text_end = Some(new_diff_range_end)
1198+ }
10731199 new_cursor.next();
10741200 }
10751201 Ordering::Equal => {
@@ -1077,7 +1203,9 @@ fn compare_hunks( · 1 unmodified line
10771203 has_changes = true;
10781204 extended_end_candidate = None;
10791205 start.get_or_insert(new_hunk.buffer_range.start);
1080− base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
1206+ base_text_start.get_or_insert(
1207+ new_base_text.anchor_before(new_hunk.diff_base_byte_range.start),
1208+ );
10811209 if old_hunk
10821210 .buffer_range
10831211 .end
@@ -1089,11 +1217,14 @@ fn compare_hunks( · 5 unmodified lines
10891217 end.replace(new_hunk.buffer_range.end);
10901218 }
10911219
1220+ let old_hunk_diff_base_range_end =
1221+ old_base_text.anchor_after(old_hunk.diff_base_byte_range.end);
1222+ let new_hunk_diff_base_range_end =
1223+ new_base_text.anchor_after(new_hunk.diff_base_byte_range.end);
1224+
10921225 base_text_end.replace(
1093− old_hunk
1094− .diff_base_byte_range
1095− .end
1096− .max(new_hunk.diff_base_byte_range.end),
1226+ *old_hunk_diff_base_range_end
1227+ .max(&new_hunk_diff_base_range_end, new_base_text),
10971228 );
10981229 } else {
10991230 if !has_changes {
@@ -1110,9 +1241,19 @@ fn compare_hunks( · 10 unmodified lines
11101241 has_changes = true;
11111242 extended_end_candidate = None;
11121243 start.get_or_insert(old_hunk.buffer_range.start);
1113− base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
1244+ base_text_start.get_or_insert(
1245+ old_base_text.anchor_after(old_hunk.diff_base_byte_range.start),
1246+ );
11141247 end.replace(old_hunk.buffer_range.end);
1115− base_text_end.replace(old_hunk.diff_base_byte_range.end);
1248+ let old_diff_range_end =
1249+ old_base_text.anchor_after(old_hunk.diff_base_byte_range.end);
1250+ if base_text_end.is_none_or(|base_text_end| {
1251+ old_diff_range_end
1252+ .cmp(&base_text_end, new_base_text)
1253+ .is_gt()
1254+ }) {
1255+ base_text_end = Some(old_diff_range_end)
1256+ }
11161257 old_cursor.next();
11171258 }
11181259 }
@@ -1121,24 +1262,38 @@ fn compare_hunks( · 2 unmodified lines
11211262 has_changes = true;
11221263 extended_end_candidate = None;
11231264 start.get_or_insert(new_hunk.buffer_range.start);
1124− base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
1265+ base_text_start
1266+ .get_or_insert(new_base_text.anchor_after(new_hunk.diff_base_byte_range.start));
11251267 if end.is_none_or(|end| end.cmp(&new_hunk.buffer_range.end, &new_snapshot).is_le())
11261268 {
11271269 end.replace(new_hunk.buffer_range.end);
11281270 }
1129− base_text_end = base_text_end.max(Some(new_hunk.diff_base_byte_range.end));
1271+ let new_base_text_end =
1272+ new_base_text.anchor_after(new_hunk.diff_base_byte_range.end);
1273+ if base_text_end.is_none_or(|base_text_end| {
1274+ new_base_text_end.cmp(&base_text_end, new_base_text).is_gt()
1275+ }) {
1276+ base_text_end = Some(new_base_text_end)
1277+ }
11301278 new_cursor.next();
11311279 }
11321280 (None, Some(old_hunk)) => {
11331281 has_changes = true;
11341282 extended_end_candidate = None;
11351283 start.get_or_insert(old_hunk.buffer_range.start);
1136− base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
1284+ base_text_start
1285+ .get_or_insert(old_base_text.anchor_after(old_hunk.diff_base_byte_range.start));
11371286 if end.is_none_or(|end| end.cmp(&old_hunk.buffer_range.end, &new_snapshot).is_le())
11381287 {
11391288 end.replace(old_hunk.buffer_range.end);
11401289 }
1141− base_text_end = base_text_end.max(Some(old_hunk.diff_base_byte_range.end));
1290+ let old_base_text_end =
1291+ old_base_text.anchor_after(old_hunk.diff_base_byte_range.end);
1292+ if base_text_end.is_none_or(|base_text_end| {
1293+ old_base_text_end.cmp(&base_text_end, new_base_text).is_gt()
1294+ }) {
1295+ base_text_end = Some(old_base_text_end);
1296+ }
11421297 old_cursor.next();
11431298 }
11441299 (None, None) => break,
@@ -1148,7 +1303,7 @@ fn compare_hunks( · 3 unmodified lines
11481303 let changed_range = start.zip(end).map(|(start, end)| start..end);
11491304 let base_text_changed_range = base_text_start
11501305 .zip(base_text_end)
1151− .map(|(start, end)| start..end);
1306+ .map(|(start, end)| (start..end).to_offset(new_base_text));
11521307
11531308 let extended_range = if has_changes && let Some(changed_range) = changed_range.clone() {
11541309 let extended_start = *last_unchanged_new_hunk_end
@@ -1524,7 +1679,7 @@ impl BufferDiff { · 369 unmodified lines
15241679 base_text
15251680 .clone()
15261681 .map(|base_text| (base_text, base_text_rope.clone())),
1527− buffer.clone(),
1682+ &buffer,
15281683 diff_options,
15291684 );
15301685 let base_text = base_text.unwrap_or_default();
@@ -1591,22 +1746,46 @@ impl BufferDiff { · 60 unmodified lines
15911746 log::debug!("set snapshot with secondary {secondary_diff_change:?}");
15921747
15931748 let old_snapshot = self.snapshot(cx);
1594− let state = &mut self.inner;
15951749 let new_state = update.inner;
15961750 let base_text_changed = update.base_text_changed;
15971751
1752+ let state = &mut self.inner;
1753+ state.base_text_exists = new_state.base_text_exists;
1754+ let should_compare_hunks = update.base_text_edits.is_some() || !base_text_changed;
1755+ let parsing_idle = if let Some(diff) = update.base_text_edits {
1756+ state.base_text.update(cx, |base_text, cx| {
1757+ base_text.set_capability(Capability::ReadWrite, cx);
1758+ base_text.apply_diff(diff, cx);
1759+ base_text.set_capability(Capability::ReadOnly, cx);
1760+ Some(base_text.parsing_idle())
1761+ })
1762+ } else if update.base_text_changed {
1763+ state.base_text.update(cx, |base_text, cx| {
1764+ base_text.set_capability(Capability::ReadWrite, cx);
1765+ base_text.set_text(new_state.base_text.clone(), cx);
1766+ base_text.set_capability(Capability::ReadOnly, cx);
1767+ Some(base_text.parsing_idle())
1768+ })
1769+ } else {
1770+ None
1771+ };
1772+
15981773 let old_buffer_snapshot = &old_snapshot.inner.buffer_snapshot;
1774+ let old_base_snapshot = &old_snapshot.inner.base_text;
1775+ let new_base_snapshot = state.base_text.read(cx).snapshot();
15991776 let DiffChanged {
16001777 mut changed_range,
16011778 mut base_text_changed_range,
16021779 mut extended_range,
16031780 } = match (state.base_text_exists, new_state.base_text_exists) {
16041781 (false, false) => DiffChanged::default(),
1605− (true, true) if !base_text_changed => compare_hunks(
1782+ (true, true) if should_compare_hunks => compare_hunks(
16061783 &new_state.hunks,
16071784 &old_snapshot.inner.hunks,
16081785 old_buffer_snapshot,
16091786 buffer,
1787+ old_base_snapshot,
1788+ &new_base_snapshot,
16101789 ),
16111790 _ => {
16121791 let full_range = text::Anchor::min_max_range_for_buffer(self.buffer_id);
@@ -1618,54 +1797,9 @@ impl BufferDiff { · 5 unmodified lines
16181797 }
16191798 }
16201799 };
1621−
1622− if let Some(secondary_changed_range) = secondary_diff_change
1623− && let (Some(secondary_hunk_range), Some(secondary_base_range)) =
1624− old_snapshot.range_to_hunk_range(secondary_changed_range, buffer)
1625− {
1626− if let Some(range) = &mut changed_range {
1627− range.start = *secondary_hunk_range.start.min(&range.start, buffer);
1628− range.end = *secondary_hunk_range.end.max(&range.end, buffer);
1629− } else {
1630− changed_range = Some(secondary_hunk_range.clone());
1631− }
1632−
1633− if let Some(base_text_range) = base_text_changed_range.as_mut() {
1634− base_text_range.start = secondary_base_range.start.min(base_text_range.start);
1635− base_text_range.end = secondary_base_range.end.max(base_text_range.end);
1636− } else {
1637− base_text_changed_range = Some(secondary_base_range);
1638− }
1639−
1640− if let Some(ext) = &mut extended_range {
1641− ext.start = *ext.start.min(&secondary_hunk_range.start, buffer);
1642− ext.end = *ext.end.max(&secondary_hunk_range.end, buffer);
1643− } else {
1644− extended_range = Some(secondary_hunk_range);
1645− }
1646− }
1647−
1648− let state = &mut self.inner;
1649− state.base_text_exists = new_state.base_text_exists;
1650− let parsing_idle = if let Some(diff) = update.base_text_edits {
1651− state.base_text.update(cx, |base_text, cx| {
1652− base_text.set_capability(Capability::ReadWrite, cx);
1653− base_text.apply_diff(diff, cx);
1654− base_text.set_capability(Capability::ReadOnly, cx);
1655− Some(base_text.parsing_idle())
1656− })
1657− } else if update.base_text_changed {
1658− state.base_text.update(cx, |base_text, cx| {
1659− base_text.set_capability(Capability::ReadWrite, cx);
1660− base_text.set_text(new_state.base_text.clone(), cx);
1661− base_text.set_capability(Capability::ReadOnly, cx);
1662− Some(base_text.parsing_idle())
1663− })
1664− } else {
1665− None
1666− };
16671800 state.hunks = new_state.hunks;
16681801 state.buffer_snapshot = update.buffer_snapshot;
1802+
16691803 if base_text_changed || clear_pending_hunks {
16701804 if let Some((first, last)) = state.pending_hunks.first().zip(state.pending_hunks.last())
16711805 {
@@ -1696,6 +1830,32 @@ impl BufferDiff { · 24 unmodified lines
16961830 state.pending_hunks = SumTree::new(buffer);
16971831 }
16981832
1833+ if let Some(secondary_changed_range) = secondary_diff_change
1834+ && let (Some(secondary_hunk_range), Some(secondary_base_range)) =
1835+ old_snapshot.range_to_hunk_range(secondary_changed_range, buffer)
1836+ {
1837+ if let Some(range) = &mut changed_range {
1838+ range.start = *secondary_hunk_range.start.min(&range.start, buffer);
1839+ range.end = *secondary_hunk_range.end.max(&range.end, buffer);
1840+ } else {
1841+ changed_range = Some(secondary_hunk_range.clone());
1842+ }
1843+
1844+ if let Some(base_text_range) = base_text_changed_range.as_mut() {
1845+ base_text_range.start = secondary_base_range.start.min(base_text_range.start);
1846+ base_text_range.end = secondary_base_range.end.max(base_text_range.end);
1847+ } else {
1848+ base_text_changed_range = Some(secondary_base_range);
1849+ }
1850+
1851+ if let Some(ext) = &mut extended_range {
1852+ ext.start = *ext.start.min(&secondary_hunk_range.start, buffer);
1853+ ext.end = *ext.end.max(&secondary_hunk_range.end, buffer);
1854+ } else {
1855+ extended_range = Some(secondary_hunk_range);
1856+ }
1857+ }
1858+
16991859 async move {
17001860 if let Some(parsing_idle) = parsing_idle {
17011861 parsing_idle.await;
@@ -1760,10 +1920,10 @@ impl BufferDiff { · 58 unmodified lines
17601920 base_text_exists: self.inner.base_text_exists,
17611921 buffer_snapshot: self.inner.buffer_snapshot.clone(),
17621922 },
1763− secondary_diff: self
1764− .secondary_diff
1765− .as_ref()
1766− .map(|diff| Box::new(diff.read(cx).snapshot(cx))),
1923+ secondary_diff: self.secondary_diff.as_ref().map(|diff| {
1924+ debug_assert!(diff.read(cx).secondary_diff.is_none());
1925+ Arc::new(diff.read(cx).snapshot(cx))
1926+ }),
17671927 }
17681928 }
17691929
@@ -1993,7 +2153,7 @@ mod tests { · 223 unmodified lines
19932153 .unindent();
19942154
19952155 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
1996− let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
2156+ let mut diff = BufferDiffSnapshot::new_sync(&buffer, diff_base.clone(), cx);
19972157 assert_hunks(
19982158 diff.hunks_intersecting_range(
19992159 Anchor::min_max_range_for_buffer(buffer.remote_id()),
@@ -2005,7 +2165,7 @@ mod tests { · 5 unmodified lines
20052165 );
20062166
20072167 buffer.edit([(0..0, "point five\n")]);
2008− diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
2168+ diff = BufferDiffSnapshot::new_sync(&buffer, diff_base.clone(), cx);
20092169 assert_hunks(
20102170 diff.hunks_intersecting_range(
20112171 Anchor::min_max_range_for_buffer(buffer.remote_id()),
@@ -2076,10 +2236,9 @@ mod tests { · 64 unmodified lines
20762236 .unindent();
20772237
20782238 let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
2079− let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
2080− let mut uncommitted_diff =
2081− BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
2082− uncommitted_diff.secondary_diff = Some(Box::new(unstaged_diff));
2239+ let unstaged_diff = BufferDiffSnapshot::new_sync(&buffer, index_text, cx);
2240+ let mut uncommitted_diff = BufferDiffSnapshot::new_sync(&buffer, head_text.clone(), cx);
2241+ uncommitted_diff.secondary_diff = Some(Arc::new(unstaged_diff));
20832242
20842243 let expected_hunks = vec![
20852244 (2..3, "two\n", "TWO\n", DiffHunkStatus::modified_none()),
@@ -2606,7 +2765,7 @@ mod tests { · 520 unmodified lines
26062765 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
26072766
26082767 let empty_diff = cx.update(|cx| BufferDiff::new(&buffer, cx).snapshot(cx));
2609− let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2768+ let diff_1 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
26102769 let DiffChanged {
26112770 changed_range,
26122771 base_text_changed_range,
@@ -2616,6 +2775,8 @@ mod tests { · 3 unmodified lines
26162775 &empty_diff.inner.hunks,
26172776 &buffer,
26182777 &buffer,
2778+ &diff_1.base_text(),
2779+ &diff_1.base_text(),
26192780 );
26202781 let range = changed_range.unwrap();
26212782 assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
@@ -2639,12 +2800,19 @@ mod tests { · 17 unmodified lines
26392800 "
26402801 .unindent(),
26412802 );
2642− let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2803+ let diff_2 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
26432804 let DiffChanged {
26442805 changed_range,
26452806 base_text_changed_range,
26462807 extended_range: _,
2647− } = compare_hunks(&diff_2.inner.hunks, &diff_1.inner.hunks, &buffer, &buffer);
2808+ } = compare_hunks(
2809+ &diff_2.inner.hunks,
2810+ &diff_1.inner.hunks,
2811+ &buffer,
2812+ &buffer,
2813+ diff_2.base_text(),
2814+ diff_2.base_text(),
2815+ );
26482816 assert_eq!(
26492817 changed_range.unwrap().to_point(&buffer),
26502818 Point::new(4, 0)..Point::new(5, 0),
@@ -2670,12 +2838,19 @@ mod tests { · 19 unmodified lines
26702838 "
26712839 .unindent(),
26722840 );
2673− let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2841+ let diff_3 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
26742842 let DiffChanged {
26752843 changed_range,
26762844 base_text_changed_range,
26772845 extended_range: _,
2678− } = compare_hunks(&diff_3.inner.hunks, &diff_2.inner.hunks, &buffer, &buffer);
2846+ } = compare_hunks(
2847+ &diff_3.inner.hunks,
2848+ &diff_2.inner.hunks,
2849+ &buffer,
2850+ &buffer,
2851+ diff_3.base_text(),
2852+ diff_3.base_text(),
2853+ );
26792854 let range = changed_range.unwrap();
26802855 assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));
26812856 let base_text_range = base_text_changed_range.unwrap();
@@ -2697,12 +2872,19 @@ mod tests { · 15 unmodified lines
26972872 "
26982873 .unindent(),
26992874 );
2700− let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2875+ let diff_4 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
27012876 let DiffChanged {
27022877 changed_range,
27032878 base_text_changed_range,
27042879 extended_range: _,
2705− } = compare_hunks(&diff_4.inner.hunks, &diff_3.inner.hunks, &buffer, &buffer);
2880+ } = compare_hunks(
2881+ &diff_4.inner.hunks,
2882+ &diff_3.inner.hunks,
2883+ &buffer,
2884+ &buffer,
2885+ diff_4.base_text(),
2886+ diff_4.base_text(),
2887+ );
27062888 let range = changed_range.unwrap();
27072889 assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));
27082890 let base_text_range = base_text_changed_range.unwrap();
@@ -2730,7 +2912,14 @@ mod tests { · 21 unmodified lines
27302912 changed_range,
27312913 base_text_changed_range,
27322914 extended_range: _,
2733− } = compare_hunks(&diff_5.inner.hunks, &diff_4.inner.hunks, &buffer, &buffer);
2915+ } = compare_hunks(
2916+ &diff_5.inner.hunks,
2917+ &diff_4.inner.hunks,
2918+ &buffer,
2919+ &buffer,
2920+ diff_5.base_text(),
2921+ diff_5.base_text(),
2922+ );
27342923 let range = changed_range.unwrap();
27352924 assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));
27362925 let base_text_range = base_text_changed_range.unwrap();
@@ -2753,12 +2942,19 @@ mod tests { · 16 unmodified lines
27532942 "
27542943 .unindent(),
27552944 );
2756− let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
2945+ let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
27572946 let DiffChanged {
27582947 changed_range,
27592948 base_text_changed_range,
27602949 extended_range: _,
2761− } = compare_hunks(&diff_6.inner.hunks, &diff_5.inner.hunks, &buffer, &buffer);
2950+ } = compare_hunks(
2951+ &diff_6.inner.hunks,
2952+ &diff_5.inner.hunks,
2953+ &buffer,
2954+ &buffer,
2955+ diff_6.base_text(),
2956+ diff_6.base_text(),
2957+ );
27622958 let range = changed_range.unwrap();
27632959 assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));
27642960 let base_text_range = base_text_changed_range.unwrap();
@@ -2766,24 +2962,91 @@ mod tests { · 1 unmodified line
27662962 base_text_range.to_point(diff_6.base_text()),
27672963 Point::new(9, 0)..Point::new(10, 0)
27682964 );
2769− }
27702965
2771− #[gpui::test(iterations = 100)]
2772− async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
2773− fn gen_line(rng: &mut StdRng) -> String {
2774− if rng.random_bool(0.2) {
2775− "\n".to_owned()
2776− } else {
2777− let c = rng.random_range('A'..='Z');
2778− format!("{c}{c}{c}\n")
2779− }
2780− }
2966+ buffer.edit_via_marked_text(
2967+ &"
2968+ one
2969+ THREE
2970+ four«»
2971+ five
2972+ seven
2973+ eight
2974+ «NINE»
2975+ "
2976+ .unindent(),
2977+ );
27812978
2782− fn gen_working_copy(rng: &mut StdRng, head: &str) -> String {
2783− let mut old_lines = {
2784− let mut old_lines = Vec::new();
2785− let old_lines_iter = head.lines();
2786− for line in old_lines_iter {
2979+ let diff_7 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
2980+ let DiffChanged {
2981+ changed_range,
2982+ base_text_changed_range,
2983+ extended_range: _,
2984+ } = compare_hunks(
2985+ &diff_7.inner.hunks,
2986+ &diff_6.inner.hunks,
2987+ &buffer,
2988+ &buffer,
2989+ diff_7.base_text(),
2990+ diff_7.base_text(),
2991+ );
2992+ let range = changed_range.unwrap();
2993+ assert_eq!(range.to_point(&buffer), Point::new(2, 4)..Point::new(7, 0));
2994+ let base_text_range = base_text_changed_range.unwrap();
2995+ assert_eq!(
2996+ base_text_range.to_point(diff_7.base_text()),
2997+ Point::new(5, 0)..Point::new(10, 0)
2998+ );
2999+
3000+ buffer.edit_via_marked_text(
3001+ &"
3002+ one
3003+ THREE
3004+ four
3005+ five«»seven
3006+ eight
3007+ NINE
3008+ "
3009+ .unindent(),
3010+ );
3011+
3012+ let diff_8 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
3013+ let DiffChanged {
3014+ changed_range,
3015+ base_text_changed_range,
3016+ extended_range: _,
3017+ } = compare_hunks(
3018+ &diff_8.inner.hunks,
3019+ &diff_7.inner.hunks,
3020+ &buffer,
3021+ &buffer,
3022+ diff_8.base_text(),
3023+ diff_8.base_text(),
3024+ );
3025+ let range = changed_range.unwrap();
3026+ assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(3, 4));
3027+ let base_text_range = base_text_changed_range.unwrap();
3028+ assert_eq!(
3029+ base_text_range.to_point(diff_8.base_text()),
3030+ Point::new(5, 0)..Point::new(8, 0)
3031+ );
3032+ }
3033+
3034+ #[gpui::test(iterations = 100)]
3035+ async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
3036+ fn gen_line(rng: &mut StdRng) -> String {
3037+ if rng.random_bool(0.2) {
3038+ "\n".to_owned()
3039+ } else {
3040+ let c = rng.random_range('A'..='Z');
3041+ format!("{c}{c}{c}\n")
3042+ }
3043+ }
3044+
3045+ fn gen_working_copy(rng: &mut StdRng, head: &str) -> String {
3046+ let mut old_lines = {
3047+ let mut old_lines = Vec::new();
3048+ let old_lines_iter = head.lines();
3049+ for line in old_lines_iter {
27873050 assert!(!line.ends_with("\n"));
27883051 old_lines.push(line.to_owned());
27893052 }
@@ -3043,11 +3306,11 @@ mod tests { · 253 unmodified lines
30433306 .unindent();
30443307
30453308 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
3046− let old_buffer = buffer.snapshot();
3047− let diff_a = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
3309+ let old_buffer = buffer.snapshot().clone();
3310+ let diff_a = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
30483311
30493312 buffer.edit([(Point::new(1, 3)..Point::new(1, 3), "\n")]);
3050− let diff_b = BufferDiffSnapshot::new_sync(buffer.clone(), base_text, cx);
3313+ let diff_b = BufferDiffSnapshot::new_sync(&buffer, base_text, cx);
30513314
30523315 let DiffChanged {
30533316 changed_range,
@@ -3058,6 +3321,8 @@ mod tests { · 4 unmodified lines
30583321 &diff_a.inner.hunks,
30593322 &old_buffer,
30603323 &buffer,
3324+ &diff_a.base_text(),
3325+ &diff_a.base_text(),
30613326 );
30623327
30633328 let changed_range = changed_range.unwrap();
@@ -3104,11 +3369,11 @@ mod tests { · 40 unmodified lines
31043369 .unindent();
31053370
31063371 let mut buffer_2 = Buffer::new(ReplicaId::LOCAL, BufferId::new(2).unwrap(), buffer_text_2);
3107− let old_buffer_2 = buffer_2.snapshot();
3108− let diff_2a = BufferDiffSnapshot::new_sync(buffer_2.clone(), base_text_2.clone(), cx);
3372+ let old_buffer_2 = buffer_2.snapshot().clone();
3373+ let diff_2a = BufferDiffSnapshot::new_sync(&buffer_2, base_text_2.clone(), cx);
31093374
31103375 buffer_2.edit([(Point::new(4, 0)..Point::new(4, 4), "FIVE_CHANGED")]);
3111− let diff_2b = BufferDiffSnapshot::new_sync(buffer_2.clone(), base_text_2, cx);
3376+ let diff_2b = BufferDiffSnapshot::new_sync(&buffer_2, base_text_2, cx);
31123377
31133378 let DiffChanged {
31143379 changed_range,
@@ -3119,6 +3384,8 @@ mod tests { · 4 unmodified lines
31193384 &diff_2a.inner.hunks,
31203385 &old_buffer_2,
31213386 &buffer_2,
3387+ &diff_2a.base_text(),
3388+ &diff_2a.base_text(),
31223389 );
31233390
31243391 let changed_range = changed_range.unwrap();
@@ -3136,1290 +3403,494 @@ mod tests { · 11 unmodified lines
31363403 );
31373404 }
31383405
3139− fn assert_rows_to_base_text_rows_visual(
3140− buffer: &Entity<language::Buffer>,
3141− diff: &Entity<BufferDiff>,
3142− source_text: &str,
3143− annotated_target: &str,
3144− cx: &mut gpui::TestAppContext,
3145− ) {
3146− let (target_text, expected_ranges) = parse_row_annotations(annotated_target);
3147−
3148− let buffer = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
3149− let diff = diff.update(cx, |diff, cx| diff.snapshot(cx));
3406+ #[gpui::test]
3407+ async fn test_buffer_diff_compare_with_base_text_change(_cx: &mut TestAppContext) {
3408+ // Use a shared base text buffer so that anchors from old and new snapshots
3409+ // share the same remote_id and resolve correctly across versions.
3410+ let initial_base = "aaa\nbbb\nccc\nddd\neee\n";
3411+ let mut base_text_buffer = Buffer::new(
3412+ ReplicaId::LOCAL,
3413+ BufferId::new(99).unwrap(),
3414+ initial_base.to_string(),
3415+ );
31503416
3151− assert_eq!(
3152− buffer.text(),
3153− source_text,
3154− "buffer text does not match source text"
3417+ // --- Scenario 1: Base text gains a line, producing a new deletion hunk ---
3418+ //
3419+ // Buffer has a modification (ccc → CCC). When the base text gains
3420+ // a new line "XXX" after "aaa", the diff now also contains a
3421+ // deletion for that line, and the modification hunk shifts in the
3422+ // base text.
3423+ let buffer_text_1 = "aaa\nbbb\nCCC\nddd\neee\n";
3424+ let buffer = Buffer::new(
3425+ ReplicaId::LOCAL,
3426+ BufferId::new(1).unwrap(),
3427+ buffer_text_1.to_string(),
31553428 );
31563429
3157− assert_eq!(
3158− diff.base_text_string().unwrap_or_default(),
3159− target_text,
3160− "base text does not match stripped annotated target"
3430+ let old_base_snapshot_1 = base_text_buffer.snapshot().clone();
3431+ let old_hunks_1 = compute_hunks(
3432+ Some((Arc::from(initial_base), Rope::from(initial_base))),
3433+ buffer.snapshot(),
3434+ None,
31613435 );
31623436
3163− let num_rows = source_text.lines().count() as u32;
3164− let max_point = buffer.max_point();
3165− let points = (0..=num_rows).map(move |row| {
3166− if row == num_rows && max_point.column > 0 {
3167− max_point
3168− } else {
3169− Point::new(row, 0)
3170− }
3171− });
3172− let actual_ranges: Vec<_> = diff.points_to_base_text_points(points, &buffer).0.collect();
3437+ // Insert "XXX\n" after "aaa\n" in the base text.
3438+ base_text_buffer.edit([(4..4, "XXX\n")]);
3439+ let new_base_str_1: Arc<str> = Arc::from(base_text_buffer.text().as_str());
3440+ let new_base_snapshot_1 = base_text_buffer.snapshot();
31733441
3174− assert_eq!(
3175− actual_ranges, expected_ranges,
3176− "\nsource (buffer):\n{}\ntarget (base):\n{}\nexpected: {:?}\nactual: {:?}",
3177− source_text, target_text, expected_ranges, actual_ranges
3442+ let new_hunks_1 = compute_hunks(
3443+ Some((new_base_str_1.clone(), Rope::from(new_base_str_1.as_ref()))),
3444+ buffer.snapshot(),
3445+ None,
31783446 );
3179− }
3180−
3181− fn assert_base_text_rows_to_rows_visual(
3182− buffer: &Entity<language::Buffer>,
3183− diff: &Entity<BufferDiff>,
3184− source_text: &str,
3185− annotated_target: &str,
3186− cx: &mut gpui::TestAppContext,
3187− ) {
3188− let (target_text, expected_ranges) = parse_row_annotations(annotated_target);
31893447
3190− let buffer = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
3191− let diff = diff.update(cx, |diff, cx| diff.snapshot(cx));
3448+ let DiffChanged {
3449+ changed_range,
3450+ base_text_changed_range,
3451+ extended_range: _,
3452+ } = compare_hunks(
3453+ &new_hunks_1,
3454+ &old_hunks_1,
3455+ &buffer.snapshot(),
3456+ &buffer.snapshot(),
3457+ &old_base_snapshot_1,
3458+ &new_base_snapshot_1,
3459+ );
31923460
3461+ // The new deletion hunk (XXX) starts at buffer row 1 and the
3462+ // modification hunk (ccc → CCC) now has a different
3463+ // diff_base_byte_range, so the changed range spans both.
3464+ let range = changed_range.unwrap();
3465+ assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(3, 0),);
3466+ let base_range = base_text_changed_range.unwrap();
31933467 assert_eq!(
3194− diff.base_text_string().unwrap_or_default(),
3195− source_text,
3196− "base text does not match source text"
3468+ base_range.to_point(&new_base_snapshot_1),
3469+ Point::new(1, 0)..Point::new(4, 0),
31973470 );
31983471
3199− assert_eq!(
3200− buffer.text(),
3201− target_text,
3202− "buffer text does not match stripped annotated target"
3472+ // --- Scenario 2: Base text changes to match the buffer (hunk disappears) ---
3473+ //
3474+ // Start fresh with a simple base text.
3475+ let simple_base = "one\ntwo\nthree\n";
3476+ let mut base_buf_2 = Buffer::new(
3477+ ReplicaId::LOCAL,
3478+ BufferId::new(100).unwrap(),
3479+ simple_base.to_string(),
32033480 );
32043481
3205− let num_rows = source_text.lines().count() as u32;
3206− let base_max_point = diff.base_text().max_point();
3207− let points = (0..=num_rows).map(move |row| {
3208− if row == num_rows && base_max_point.column > 0 {
3209− base_max_point
3210− } else {
3211− Point::new(row, 0)
3212− }
3213− });
3214− let actual_ranges: Vec<_> = diff.base_text_points_to_points(points, &buffer).0.collect();
3482+ let buffer_text_2 = "one\nTWO\nthree\n";
3483+ let buffer_2 = Buffer::new(
3484+ ReplicaId::LOCAL,
3485+ BufferId::new(2).unwrap(),
3486+ buffer_text_2.to_string(),
3487+ );
32153488
3216− assert_eq!(
3217− actual_ranges, expected_ranges,
3218− "\nsource (base):\n{}\ntarget (buffer):\n{}\nexpected: {:?}\nactual: {:?}",
3219− source_text, target_text, expected_ranges, actual_ranges
3489+ let old_base_snapshot_2 = base_buf_2.snapshot().clone();
3490+ let old_hunks_2 = compute_hunks(
3491+ Some((Arc::from(simple_base), Rope::from(simple_base))),
3492+ buffer_2.snapshot(),
3493+ None,
32203494 );
3221− }
32223495
3223− fn parse_row_annotations(annotated_text: &str) -> (String, Vec<Range<Point>>) {
3224− let mut starts: std::collections::HashMap<u32, Point> = std::collections::HashMap::new();
3225− let mut ends: std::collections::HashMap<u32, Point> = std::collections::HashMap::new();
3496+ // The base text is edited so "two" becomes "TWO", now matching the buffer.
3497+ base_buf_2.edit([(4..7, "TWO")]);
3498+ let new_base_str_2: Arc<str> = Arc::from(base_buf_2.text().as_str());
3499+ let new_base_snapshot_2 = base_buf_2.snapshot();
32263500
3227− let mut clean_text = String::new();
3228− let mut current_point = Point::new(0, 0);
3229− let mut chars = annotated_text.chars().peekable();
3501+ let new_hunks_2 = compute_hunks(
3502+ Some((new_base_str_2.clone(), Rope::from(new_base_str_2.as_ref()))),
3503+ buffer_2.snapshot(),
3504+ None,
3505+ );
32303506
3231− while let Some(c) = chars.next() {
3232− if c == '<' {
3233− let mut num_str = String::new();
3234− while let Some(&next) = chars.peek() {
3235− if next.is_ascii_digit() {
3236− num_str.push(chars.next().unwrap());
3237− } else {
3238− break;
3239− }
3240− }
3241− if !num_str.is_empty() {
3242− let row_num: u32 = num_str.parse().unwrap();
3243− starts.insert(row_num, current_point);
3507+ let DiffChanged {
3508+ changed_range,
3509+ base_text_changed_range,
3510+ extended_range: _,
3511+ } = compare_hunks(
3512+ &new_hunks_2,
3513+ &old_hunks_2,
3514+ &buffer_2.snapshot(),
3515+ &buffer_2.snapshot(),
3516+ &old_base_snapshot_2,
3517+ &new_base_snapshot_2,
3518+ );
32443519
3245− if chars.peek() == Some(&'>') {
3246− chars.next();
3247− ends.insert(row_num, current_point);
3248− }
3249− } else {
3250− clean_text.push(c);
3251− current_point.column += 1;
3252− }
3253− } else if c.is_ascii_digit() {
3254− let mut num_str = String::from(c);
3255− while let Some(&next) = chars.peek() {
3256− if next.is_ascii_digit() {
3257− num_str.push(chars.next().unwrap());
3258− } else {
3259− break;
3260− }
3261− }
3262− if chars.peek() == Some(&'>') {
3263− chars.next();
3264− let row_num: u32 = num_str.parse().unwrap();
3265− ends.insert(row_num, current_point);
3266− } else {
3267− for ch in num_str.chars() {
3268− clean_text.push(ch);
3269− current_point.column += 1;
3270− }
3271− }
3272− } else if c == '\n' {
3273− clean_text.push(c);
3274− current_point.row += 1;
3275− current_point.column = 0;
3276− } else {
3277− clean_text.push(c);
3278− current_point.column += 1;
3279− }
3280− }
3520+ // The old modification hunk (two → TWO) is now gone because the
3521+ // base text matches the buffer. The changed range covers where the
3522+ // old hunk used to be.
3523+ let range = changed_range.unwrap();
3524+ assert_eq!(
3525+ range.to_point(&buffer_2),
3526+ Point::new(1, 0)..Point::new(2, 0),
3527+ );
3528+ let base_range = base_text_changed_range.unwrap();
3529+ // The old hunk's diff_base_byte_range covered "two\n" (bytes 4..8).
3530+ // anchor_after(4) is right-biased at the start of the deleted "two",
3531+ // so after the edit replacing "two" with "TWO" it resolves past the
3532+ // insertion to Point(1, 3).
3533+ assert_eq!(
3534+ base_range.to_point(&new_base_snapshot_2),
3535+ Point::new(1, 3)..Point::new(2, 0),
3536+ );
32813537
3282− let max_row = starts.keys().chain(ends.keys()).max().copied().unwrap_or(0);
3283− let mut ranges: Vec<Range<Point>> = Vec::new();
3284− for row in 0..=max_row {
3285− let start = starts.get(&row).copied().unwrap_or(Point::new(0, 0));
3286− let end = ends.get(&row).copied().unwrap_or(start);
3287− ranges.push(start..end);
3288− }
3538+ // --- Scenario 3: Base text edit changes one hunk but not another ---
3539+ //
3540+ // Two modification hunks exist. Only one of them is resolved by
3541+ // the base text change; the other remains identical.
3542+ let base_3 = "aaa\nbbb\nccc\nddd\neee\n";
3543+ let mut base_buf_3 = Buffer::new(
3544+ ReplicaId::LOCAL,
3545+ BufferId::new(101).unwrap(),
3546+ base_3.to_string(),
3547+ );
32893548
3290− (clean_text, ranges)
3291− }
3549+ let buffer_text_3 = "aaa\nBBB\nccc\nDDD\neee\n";
3550+ let buffer_3 = Buffer::new(
3551+ ReplicaId::LOCAL,
3552+ BufferId::new(3).unwrap(),
3553+ buffer_text_3.to_string(),
3554+ );
32923555
3293− fn make_diff(
3294− base_text: &str,
3295− buffer_text: &str,
3296− cx: &mut gpui::TestAppContext,
3297− ) -> (Entity<language::Buffer>, Entity<BufferDiff>) {
3298− let buffer = cx.new(|cx| language::Buffer::local(buffer_text, cx));
3299− let diff = cx.new(|cx| {
3300− BufferDiff::new_with_base_text(base_text, &buffer.read(cx).text_snapshot(), cx)
3301− });
3302− (buffer, diff)
3303− }
3556+ let old_base_snapshot_3 = base_buf_3.snapshot().clone();
3557+ let old_hunks_3 = compute_hunks(
3558+ Some((Arc::from(base_3), Rope::from(base_3))),
3559+ buffer_3.snapshot(),
3560+ None,
3561+ );
33043562
3305− #[gpui::test]
3306− async fn test_row_translation_visual(cx: &mut gpui::TestAppContext) {
3307− use unindent::Unindent;
3563+ // Change "ddd" to "DDD" in the base text so that hunk disappears,
3564+ // but "bbb" stays, so its hunk remains.
3565+ base_buf_3.edit([(12..15, "DDD")]);
3566+ let new_base_str_3: Arc<str> = Arc::from(base_buf_3.text().as_str());
3567+ let new_base_snapshot_3 = base_buf_3.snapshot();
33083568
3309− {
3310− let buffer_text = "
3311− aaa
3312− bbb
3313− ccc
3314− "
3315− .unindent();
3316− let annotated_base = "
3317− <0>aaa
3318− <1>bbb
3319− <2>ccc
3320− <3>"
3321− .unindent();
3322− let (base_text, _) = parse_row_annotations(&annotated_base);
3323− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3324− assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
3325− }
3569+ let new_hunks_3 = compute_hunks(
3570+ Some((new_base_str_3.clone(), Rope::from(new_base_str_3.as_ref()))),
3571+ buffer_3.snapshot(),
3572+ None,
3573+ );
33263574
3327− {
3328− let base_text = "
3329− aaa
3330− bbb
3331− ccc
3332− "
3333− .unindent();
3334− let annotated_buffer = "
3335− <0>aaa
3336− <1>bbb
3337− <2>ccc
3338− <3>"
3339− .unindent();
3340− let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
3341− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3342− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3343− }
3575+ let DiffChanged {
3576+ changed_range,
3577+ base_text_changed_range,
3578+ extended_range: _,
3579+ } = compare_hunks(
3580+ &new_hunks_3,
3581+ &old_hunks_3,
3582+ &buffer_3.snapshot(),
3583+ &buffer_3.snapshot(),
3584+ &old_base_snapshot_3,
3585+ &new_base_snapshot_3,
3586+ );
33443587
3345− {
3346− let buffer_text = "
3347− XXX
3348− bbb
3349− ccc
3350− "
3351− .unindent();
3352− let annotated_base = "
3353− <0<1aaa
3354− 0>1>bbb
3355− <2>ccc
3356− <3>"
3357− .unindent();
3358− let (base_text, _) = parse_row_annotations(&annotated_base);
3359− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3360− assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
3361− }
3588+ // Only the second hunk (ddd → DDD) disappeared; the first hunk
3589+ // (bbb → BBB) is unchanged, so the changed range covers only line 3.
3590+ let range = changed_range.unwrap();
3591+ assert_eq!(
3592+ range.to_point(&buffer_3),
3593+ Point::new(3, 0)..Point::new(4, 0),
3594+ );
3595+ let base_range = base_text_changed_range.unwrap();
3596+ // anchor_after(12) is right-biased at the start of deleted "ddd",
3597+ // so after the edit replacing "ddd" with "DDD" it resolves past
3598+ // the insertion to Point(3, 3).
3599+ assert_eq!(
3600+ base_range.to_point(&new_base_snapshot_3),
3601+ Point::new(3, 3)..Point::new(4, 0),
3602+ );
33623603
3363− {
3364− let buffer_text = "
3365− aaa
3366− NEW
3367− ccc
3368− "
3369− .unindent();
3370− let annotated_base = "
3371− <0>aaa
3372− <1><2>ccc
3373− <3>"
3374− .unindent();
3375− let (base_text, _) = parse_row_annotations(&annotated_base);
3376− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3377− assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
3378− }
3604+ // --- Scenario 4: Both buffer and base text change simultaneously ---
3605+ //
3606+ // The buffer gains an edit that introduces a new hunk while the
3607+ // base text also changes.
3608+ let base_4 = "alpha\nbeta\ngamma\ndelta\n";
3609+ let mut base_buf_4 = Buffer::new(
3610+ ReplicaId::LOCAL,
3611+ BufferId::new(102).unwrap(),
3612+ base_4.to_string(),
3613+ );
33793614
3380− {
3381− let base_text = "
3382− aaa
3383− ccc
3384− "
3385− .unindent();
3386− let annotated_buffer = "
3387− <0>aaa
3388− <1NEW
3389− 1>ccc
3390− <2>"
3391− .unindent();
3392− let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
3393− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3394− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3395− }
3615+ let buffer_text_4 = "alpha\nBETA\ngamma\ndelta\n";
3616+ let mut buffer_4 = Buffer::new(
3617+ ReplicaId::LOCAL,
3618+ BufferId::new(4).unwrap(),
3619+ buffer_text_4.to_string(),
3620+ );
33963621
3397− {
3398− let buffer_text = "aaa\nbbb";
3399− let annotated_base = "<0>aaa\n<1>bbb<2>";
3400− let (base_text, _) = parse_row_annotations(annotated_base);
3401− let (buffer, diff) = make_diff(&base_text, buffer_text, cx);
3402− assert_rows_to_base_text_rows_visual(&buffer, &diff, buffer_text, annotated_base, cx);
3403− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, annotated_base, cx);
3404− }
3622+ let old_base_snapshot_4 = base_buf_4.snapshot().clone();
3623+ let old_buffer_snapshot_4 = buffer_4.snapshot().clone();
3624+ let old_hunks_4 = compute_hunks(
3625+ Some((Arc::from(base_4), Rope::from(base_4))),
3626+ buffer_4.snapshot(),
3627+ None,
3628+ );
34053629
3406− {
3407− let base_text = "
3408− aaa
3409− bbb
3410− ccc
3630+ // Edit the buffer: change "delta" to "DELTA" (new modification hunk).
3631+ buffer_4.edit_via_marked_text(
3632+ &"
3633+ alpha
3634+ BETA
3635+ gamma
3636+ «DELTA»
34113637 "
3412− .unindent();
3413− let annotated_buffer = "
3414− <0<1XXX
3415− 0>1>bbb
3416− <2>ccc
3417− <3>"
3418− .unindent();
3419− let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
3420− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3421− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3422− }
3638+ .unindent(),
3639+ );
34233640
3424− {
3425− let buffer_text = "
3426− aaa
3427− bbb
3428− XXX
3429− "
3430− .unindent();
3431− let annotated_base = "
3432− <0>aaa
3433− <1>bbb
3434− <2<3ccc
3435− 2>3>"
3436− .unindent();
3437− let (base_text, _) = parse_row_annotations(&annotated_base);
3438− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3439− assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
3440− }
3641+ // Edit the base text: change "beta" to "BETA" (resolves that hunk).
3642+ base_buf_4.edit([(6..10, "BETA")]);
3643+ let new_base_str_4: Arc<str> = Arc::from(base_buf_4.text().as_str());
3644+ let new_base_snapshot_4 = base_buf_4.snapshot();
34413645
3442− {
3443− let base_text = "
3444− aaa
3445− bbb
3446− ccc
3447− "
3448− .unindent();
3449− let annotated_buffer = "
3450− <0>aaa
3451− <1>bbb
3452− <2<3XXX
3453− 2>3>"
3454− .unindent();
3455− let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
3456− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3457− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3458− }
3646+ let new_hunks_4 = compute_hunks(
3647+ Some((new_base_str_4.clone(), Rope::from(new_base_str_4.as_ref()))),
3648+ buffer_4.snapshot(),
3649+ None,
3650+ );
34593651
3460− {
3461− let buffer_text = "
3462− aaa
3463− ccc
3464− "
3465− .unindent();
3466− let annotated_base = "
3467− <0>aaa
3468− <1DELETED
3469− 1>ccc
3470− <2>"
3471− .unindent();
3472− let (base_text, _) = parse_row_annotations(&annotated_base);
3473− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3474− assert_rows_to_base_text_rows_visual(&buffer, &diff, &buffer_text, &annotated_base, cx);
3475− }
3652+ let DiffChanged {
3653+ changed_range,
3654+ base_text_changed_range,
3655+ extended_range: _,
3656+ } = compare_hunks(
3657+ &new_hunks_4,
3658+ &old_hunks_4,
3659+ &old_buffer_snapshot_4,
3660+ &buffer_4.snapshot(),
3661+ &old_base_snapshot_4,
3662+ &new_base_snapshot_4,
3663+ );
34763664
3477− {
3478− let base_text = "
3479− aaa
3480− DELETED
3481− ccc
3482− "
3483− .unindent();
3484− let annotated_buffer = "
3485− <0>aaa
3486− <1><2>ccc
3487− <3>"
3488− .unindent();
3489− let (buffer_text, _) = parse_row_annotations(&annotated_buffer);
3490− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3491− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3492− }
3665+ // The old BETA hunk (line 1) is gone and a new DELTA hunk (line 3)
3666+ // appeared, so the changed range spans from line 1 through line 4.
3667+ let range = changed_range.unwrap();
3668+ assert_eq!(
3669+ range.to_point(&buffer_4),
3670+ Point::new(1, 0)..Point::new(4, 0),
3671+ );
3672+ let base_range = base_text_changed_range.unwrap();
3673+ // The old BETA hunk's base range started at byte 6 ("beta"). After
3674+ // the base text edit replacing "beta" with "BETA", anchor_after(6)
3675+ // resolves past the insertion to Point(1, 4).
3676+ assert_eq!(
3677+ base_range.to_point(&new_base_snapshot_4),
3678+ Point::new(1, 4)..Point::new(4, 0),
3679+ );
34933680 }
34943681
3495− #[gpui::test]
3496− async fn test_row_translation_with_edits_since_diff(cx: &mut gpui::TestAppContext) {
3497− use unindent::Unindent;
3498−
3499− {
3500− let base_text = "
3501− aaa
3502− bbb
3503− ccc
3504− "
3505− .unindent();
3506− let buffer_text = base_text.clone();
3507− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3508−
3509− buffer.update(cx, |buffer, cx| {
3510− buffer.edit([(4..7, "XXX")], None, cx);
3511− });
3512−
3513− let new_buffer_text = "
3514− aaa
3515− XXX
3516− ccc
3517− "
3518− .unindent();
3519− let annotated_base = "
3520− <0>aaa
3521− <1bbb1>
3522− <2>ccc
3523− <3>"
3524− .unindent();
3525− assert_rows_to_base_text_rows_visual(
3526− &buffer,
3527− &diff,
3528− &new_buffer_text,
3529− &annotated_base,
3530− cx,
3531− );
3532− }
3533−
3534− {
3535− let base_text = "
3536− aaa
3537− bbb
3538− ccc
3539− "
3540− .unindent();
3541− let buffer_text = base_text.clone();
3542− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3543−
3544− buffer.update(cx, |buffer, cx| {
3545− buffer.edit([(4..7, "XXX")], None, cx);
3546− });
3547−
3548− let annotated_buffer = "
3549− <0>aaa
3550− <1XXX1>
3551− <2>ccc
3552− <3>"
3553− .unindent();
3554− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3555− }
3556−
3557− {
3558− let base_text = "
3559− aaa
3560− bbb
3561− ccc
3562− "
3563− .unindent();
3564− let buffer_text = base_text.clone();
3565− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3566−
3567− buffer.update(cx, |buffer, cx| {
3568− buffer.edit([(4..4, "NEW\n")], None, cx);
3569− });
3570−
3571− let new_buffer_text = "
3572− aaa
3573− NEW
3574− bbb
3575− ccc
3576− "
3577− .unindent();
3578− let annotated_base = "
3579− <0>aaa
3580− <1><2>bbb
3581− <3>ccc
3582− <4>"
3583− .unindent();
3584− assert_rows_to_base_text_rows_visual(
3585− &buffer,
3586− &diff,
3587− &new_buffer_text,
3588− &annotated_base,
3589− cx,
3590− );
3591− }
3592−
3593− {
3594− let base_text = "
3595− aaa
3596− bbb
3597− ccc
3598− "
3599− .unindent();
3600− let buffer_text = base_text.clone();
3601− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3602−
3603− buffer.update(cx, |buffer, cx| {
3604− buffer.edit([(4..4, "NEW\n")], None, cx);
3605− });
3606−
3607− let annotated_buffer = "
3608− <0>aaa
3609− <1NEW
3610− 1>bbb
3611− <2>ccc
3612− <3>"
3613− .unindent();
3614− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3615− }
3616−
3617− {
3618− let base_text = "
3619− aaa
3620− bbb
3621− ccc
3622− "
3623− .unindent();
3624− let buffer_text = base_text.clone();
3625− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3626−
3627− buffer.update(cx, |buffer, cx| {
3628− buffer.edit([(4..8, "")], None, cx);
3629− });
3630−
3631− let new_buffer_text = "
3632− aaa
3633− ccc
3634− "
3635− .unindent();
3636− let annotated_base = "
3637− <0>aaa
3638− <1bbb
3639− 1>ccc
3640− <2>"
3641− .unindent();
3642− assert_rows_to_base_text_rows_visual(
3643− &buffer,
3644− &diff,
3645− &new_buffer_text,
3646− &annotated_base,
3647− cx,
3648− );
3649− }
3650−
3651− {
3652− let base_text = "
3653− aaa
3654− bbb
3655− ccc
3656− "
3657− .unindent();
3658− let buffer_text = base_text.clone();
3659− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3660−
3661− buffer.update(cx, |buffer, cx| {
3662− buffer.edit([(4..8, "")], None, cx);
3663− });
3664−
3665− let annotated_buffer = "
3666− <0>aaa
3667− <1><2>ccc
3668− <3>"
3669− .unindent();
3670− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3671− }
3672−
3673− {
3674− let base_text = "
3675− aaa
3676− bbb
3677− ccc
3678− ddd
3679− eee
3680− "
3681− .unindent();
3682− let buffer_text = "
3683− aaa
3684− XXX
3685− ccc
3686− ddd
3687− eee
3688− "
3689− .unindent();
3690− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3691−
3692− buffer.update(cx, |buffer, cx| {
3693− buffer.edit([(12..15, "YYY")], None, cx);
3694− });
3695−
3696− let new_buffer_text = "
3697− aaa
3698− XXX
3699− ccc
3700− YYY
3701− eee
3702− "
3703− .unindent();
3704− let annotated_base = "
3705− <0>aaa
3706− <1<2bbb
3707− 1>2>ccc
3708− <3ddd3>
3709− <4>eee
3710− <5>"
3711− .unindent();
3712− assert_rows_to_base_text_rows_visual(
3713− &buffer,
3714− &diff,
3715− &new_buffer_text,
3716− &annotated_base,
3717− cx,
3718− );
3719− }
3720−
3721− {
3722− let base_text = "
3723− aaa
3724− bbb
3725− ccc
3726− ddd
3727− eee
3728− "
3729− .unindent();
3730− let buffer_text = "
3731− aaa
3732− XXX
3733− ccc
3734− ddd
3735− eee
3736− "
3737− .unindent();
3738− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3739−
3740− buffer.update(cx, |buffer, cx| {
3741− buffer.edit([(12..15, "YYY")], None, cx);
3742− });
3743−
3744− let annotated_buffer = "
3745− <0>aaa
3746− <1<2XXX
3747− 1>2>ccc
3748− <3YYY3>
3749− <4>eee
3750− <5>"
3751− .unindent();
3752− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3753− }
3754−
3755− {
3756− let base_text = "
3757− aaa
3758− bbb
3759− ccc
3760− "
3761− .unindent();
3762− let buffer_text = base_text.clone();
3763− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3764−
3765− buffer.update(cx, |buffer, cx| {
3766− buffer.edit([(0..0, "NEW\n")], None, cx);
3767− });
3768−
3769− let new_buffer_text = "
3770− NEW
3771− aaa
3772− bbb
3773− ccc
3774− "
3775− .unindent();
3776− let annotated_base = "
3777− <0><1>aaa
3778− <2>bbb
3779− <3>ccc
3780− <4>"
3781− .unindent();
3782− assert_rows_to_base_text_rows_visual(
3783− &buffer,
3784− &diff,
3785− &new_buffer_text,
3786− &annotated_base,
3787− cx,
3788− );
3789− }
3790−
3791− {
3792− let base_text = "
3793− aaa
3794− bbb
3795− ccc
3796− "
3797− .unindent();
3798− let buffer_text = base_text.clone();
3799− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3800−
3801− buffer.update(cx, |buffer, cx| {
3802− buffer.edit([(0..0, "NEW\n")], None, cx);
3803− });
3804−
3805− let annotated_buffer = "
3806− <0NEW
3807− 0>aaa
3808− <1>bbb
3809− <2>ccc
3810− <3>"
3811− .unindent();
3812− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3813− }
3814−
3815− {
3816− let base_text = "
3817− aaa
3818− bbb
3819− ccc
3820− "
3821− .unindent();
3822− let buffer_text = base_text.clone();
3823− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3824−
3825− buffer.update(cx, |buffer, cx| {
3826− buffer.edit([(12..12, "NEW\n")], None, cx);
3827− });
3828−
3829− let new_buffer_text = "
3830− aaa
3831− bbb
3832− ccc
3833− NEW
3834− "
3835− .unindent();
3836− let annotated_base = "
3837− <0>aaa
3838− <1>bbb
3839− <2>ccc
3840− <3><4>"
3841− .unindent();
3842− assert_rows_to_base_text_rows_visual(
3843− &buffer,
3844− &diff,
3845− &new_buffer_text,
3846− &annotated_base,
3847− cx,
3848− );
3849− }
3850−
3851− {
3852− let base_text = "
3853− aaa
3854− bbb
3855− ccc
3856− "
3857− .unindent();
3858− let buffer_text = base_text.clone();
3859− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3860−
3861− buffer.update(cx, |buffer, cx| {
3862− buffer.edit([(12..12, "NEW\n")], None, cx);
3863− });
3864−
3865− let annotated_buffer = "
3866− <0>aaa
3867− <1>bbb
3868− <2>ccc
3869− <3NEW
3870− 3>"
3871− .unindent();
3872− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3873− }
3874−
3875− {
3876− let base_text = "";
3877− let buffer_text = "aaa\n";
3878− let (buffer, diff) = make_diff(base_text, buffer_text, cx);
3879−
3880− buffer.update(cx, |buffer, cx| {
3881− buffer.edit([(4..4, "bbb\n")], None, cx);
3882− });
3883−
3884− let new_buffer_text = "
3885− aaa
3886− bbb
3887− "
3888− .unindent();
3889− let annotated_base = "<0><1><2>";
3890− assert_rows_to_base_text_rows_visual(
3891− &buffer,
3892− &diff,
3893− &new_buffer_text,
3894− &annotated_base,
3895− cx,
3896− );
3682+ #[gpui::test(iterations = 100)]
3683+ async fn test_patch_for_range_random(cx: &mut TestAppContext, mut rng: StdRng) {
3684+ fn gen_line(rng: &mut StdRng) -> String {
3685+ if rng.random_bool(0.2) {
3686+ "\n".to_owned()
3687+ } else {
3688+ let c = rng.random_range('A'..='Z');
3689+ format!("{c}{c}{c}\n")
3690+ }
38973691 }
38983692
3899− {
3900− let base_text = "aaa\n";
3901− let buffer_text = "";
3902− let (buffer, diff) = make_diff(base_text, buffer_text, cx);
3903−
3904− buffer.update(cx, |buffer, cx| {
3905− buffer.edit([(0..0, "bbb\n")], None, cx);
3906− });
3907−
3908− let new_buffer_text = "bbb\n";
3909− let annotated_base = "
3910− <0<1aaa
3911− 0>1>"
3912− .unindent();
3913− assert_rows_to_base_text_rows_visual(
3914− &buffer,
3915− &diff,
3916− &new_buffer_text,
3917− &annotated_base,
3918− cx,
3919− );
3693+ fn gen_text(rng: &mut StdRng, line_count: usize) -> String {
3694+ (0..line_count).map(|_| gen_line(rng)).collect()
39203695 }
39213696
3922− {
3923− let base_text = "";
3924− let buffer_text = "";
3925− let (buffer, diff) = make_diff(base_text, buffer_text, cx);
3697+ fn gen_edits_from(rng: &mut StdRng, base: &str) -> String {
3698+ let mut old_lines: Vec<&str> = base.lines().collect();
3699+ let mut result = String::new();
39263700
3927− buffer.update(cx, |buffer, cx| {
3928− buffer.edit([(0..0, "aaa\n")], None, cx);
3929− });
3701+ while !old_lines.is_empty() {
3702+ let unchanged_count = rng.random_range(0..=old_lines.len());
3703+ for _ in 0..unchanged_count {
3704+ if old_lines.is_empty() {
3705+ break;
3706+ }
3707+ result.push_str(old_lines.remove(0));
3708+ result.push('\n');
3709+ }
39303710
3931− let new_buffer_text = "aaa\n";
3932− let annotated_base = "<0><1>";
3933− assert_rows_to_base_text_rows_visual(
3934− &buffer,
3935− &diff,
3936− &new_buffer_text,
3937− &annotated_base,
3938− cx,
3939− );
3940− }
3711+ if old_lines.is_empty() {
3712+ break;
3713+ }
39413714
3942− {
3943− let base_text = "
3944− aaa
3945− bbb
3946− ccc
3947− "
3948− .unindent();
3949− let buffer_text = "
3950− aaa
3951− XXX
3952− ccc
3953− "
3954− .unindent();
3955− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3715+ let deleted_count = rng.random_range(0..=old_lines.len().min(3));
3716+ for _ in 0..deleted_count {
3717+ if old_lines.is_empty() {
3718+ break;
3719+ }
3720+ old_lines.remove(0);
3721+ }
39563722
3957− buffer.update(cx, |buffer, cx| {
3958− buffer.edit([(4..7, "YYY")], None, cx);
3959− });
3723+ let minimum_added = if deleted_count == 0 { 1 } else { 0 };
3724+ let added_count = rng.random_range(minimum_added..=3);
3725+ for _ in 0..added_count {
3726+ result.push_str(&gen_line(rng));
3727+ }
3728+ }
39603729
3961− let new_buffer_text = "
3962− aaa
3963− YYY
3964− ccc
3965− "
3966− .unindent();
3967− let annotated_base = "
3968− <0>aaa
3969− <1<2bbb
3970− 1>2>ccc
3971− <3>"
3972− .unindent();
3973− assert_rows_to_base_text_rows_visual(
3974− &buffer,
3975− &diff,
3976− &new_buffer_text,
3977− &annotated_base,
3978− cx,
3979− );
3730+ result
39803731 }
39813732
3982− {
3983− let base_text = "
3984− aaa
3985− bbb
3986− ccc
3987− "
3988− .unindent();
3989− let buffer_text = "
3990− aaa
3991− XXX
3992− ccc
3993− "
3994− .unindent();
3995− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3996−
3997− buffer.update(cx, |buffer, cx| {
3998− buffer.edit([(4..7, "YYY")], None, cx);
3999− });
4000−
4001− let annotated_buffer = "
4002− <0>aaa
4003− <1<2YYY
4004− 1>2>ccc
4005− <3>"
4006− .unindent();
4007− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3733+ fn random_point_in_text(rng: &mut StdRng, lines: &[&str]) -> Point {
3734+ if lines.is_empty() {
3735+ return Point::zero();
3736+ }
3737+ let row = rng.random_range(0..lines.len() as u32);
3738+ let line = lines[row as usize];
3739+ let col = if line.is_empty() {
3740+ 0
3741+ } else {
3742+ rng.random_range(0..=line.len() as u32)
3743+ };
3744+ Point::new(row, col)
40083745 }
40093746
4010− {
4011− let base_text = "
4012− aaa
4013− bbb
4014− ccc
4015− "
4016− .unindent();
4017− let buffer_text = "
4018− aaa
4019− XXXX
4020− ccc
4021− "
4022− .unindent();
4023− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
4024−
4025− buffer.update(cx, |buffer, cx| {
4026− buffer.edit([(4..6, "YY")], None, cx);
4027− });
4028−
4029− let new_buffer_text = "
4030− aaa
4031− YYXX
4032− ccc
4033− "
4034− .unindent();
4035− let annotated_base = "
4036− <0>aaa
4037− <1<2bbb
4038− 1>2>ccc
4039− <3>"
4040− .unindent();
4041− assert_rows_to_base_text_rows_visual(
4042− &buffer,
4043− &diff,
4044− &new_buffer_text,
4045− &annotated_base,
4046− cx,
4047− );
3747+ fn random_range_in_text(rng: &mut StdRng, lines: &[&str]) -> RangeInclusive<Point> {
3748+ let start = random_point_in_text(rng, lines);
3749+ let end = random_point_in_text(rng, lines);
3750+ if start <= end {
3751+ start..=end
3752+ } else {
3753+ end..=start
3754+ }
40483755 }
40493756
4050− {
4051− let base_text = "
4052− aaa
4053− bbb
4054− ccc
4055− "
4056− .unindent();
4057− let buffer_text = "
4058− aaa
4059− XXXX
4060− ccc
4061− "
4062− .unindent();
4063− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
4064−
4065− buffer.update(cx, |buffer, cx| {
4066− buffer.edit([(6..8, "YY")], None, cx);
4067− });
4068−
4069− let new_buffer_text = "
4070− aaa
4071− XXYY
4072− ccc
4073− "
4074− .unindent();
4075− let annotated_base = "
4076− <0>aaa
4077− <1<2bbb
4078− 1>2>ccc
4079− <3>"
4080− .unindent();
4081− assert_rows_to_base_text_rows_visual(
4082− &buffer,
4083− &diff,
4084− &new_buffer_text,
4085− &annotated_base,
4086− cx,
4087− );
3757+ fn points_in_range(range: &RangeInclusive<Point>, lines: &[&str]) -> Vec<Point> {
3758+ let mut points = Vec::new();
3759+ for row in range.start().row..=range.end().row {
3760+ if row as usize >= lines.len() {
3761+ points.push(Point::new(row, 0));
3762+ continue;
3763+ }
3764+ let line = lines[row as usize];
3765+ let start_col = if row == range.start().row {
3766+ range.start().column
3767+ } else {
3768+ 0
3769+ };
3770+ let end_col = if row == range.end().row {
3771+ range.end().column
3772+ } else {
3773+ line.len() as u32
3774+ };
3775+ for col in start_col..=end_col {
3776+ points.push(Point::new(row, col));
3777+ }
3778+ }
3779+ points
40883780 }
40893781
4090− {
4091− let base_text = "
4092− aaa
4093− bbb
4094− ccc
4095− "
4096− .unindent();
4097− let buffer_text = "
4098− aaa
4099− XXX
4100− ccc
4101− "
4102− .unindent();
4103− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
4104−
4105− buffer.update(cx, |buffer, cx| {
4106− buffer.edit([(4..4, "NEW")], None, cx);
4107− });
4108−
4109− let new_buffer_text = "
4110− aaa
4111− NEWXXX
4112− ccc
4113− "
4114− .unindent();
4115− let annotated_base = "
4116− <0>aaa
4117− <1<2bbb
4118− 1>2>ccc
4119− <3>"
4120− .unindent();
4121− assert_rows_to_base_text_rows_visual(
4122− &buffer,
4123− &diff,
4124− &new_buffer_text,
4125− &annotated_base,
4126− cx,
4127− );
4128− }
3782+ let rng = &mut rng;
41293783
4130− {
4131− let base_text = "
4132− aaa
4133− bbb
4134− ccc
4135− "
4136− .unindent();
4137− let buffer_text = "
4138− aaa
4139− XXX
4140− ccc
4141− "
4142− .unindent();
4143− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3784+ let line_count = rng.random_range(5..20);
3785+ let base_text = gen_text(rng, line_count);
3786+ let initial_buffer_text = gen_edits_from(rng, &base_text);
41443787
4145− buffer.update(cx, |buffer, cx| {
4146− buffer.edit([(7..7, "NEW")], None, cx);
4147− });
3788+ let mut buffer = Buffer::new(
3789+ ReplicaId::LOCAL,
3790+ BufferId::new(1).unwrap(),
3791+ initial_buffer_text.clone(),
3792+ );
41483793
4149− let new_buffer_text = "
4150− aaa
4151− XXXNEW
4152− ccc
4153− "
4154− .unindent();
4155− let annotated_base = "
4156− <0>aaa
4157− <1<2bbb
4158− 1>2>ccc
4159− <3>"
4160− .unindent();
4161− assert_rows_to_base_text_rows_visual(
4162− &buffer,
4163− &diff,
4164− &new_buffer_text,
4165− &annotated_base,
4166− cx,
4167− );
4168− }
3794+ let diff = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
41693795
4170− {
4171− let base_text = "
4172− aaa
4173− bbb
4174− ccc
4175− "
4176− .unindent();
4177− let buffer_text = "
4178− aaa
4179− ccc
4180− "
4181− .unindent();
4182− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3796+ let edit_count = rng.random_range(1..=5);
3797+ for _ in 0..edit_count {
3798+ let buffer_text = buffer.text();
3799+ if buffer_text.is_empty() {
3800+ buffer.edit([(0..0, gen_line(rng))]);
3801+ } else {
3802+ let lines: Vec<&str> = buffer_text.lines().collect();
3803+ let start_row = rng.random_range(0..lines.len());
3804+ let end_row = rng.random_range(start_row..=lines.len().min(start_row + 3));
41833805
4184− buffer.update(cx, |buffer, cx| {
4185− buffer.edit([(4..4, "NEW\n")], None, cx);
4186− });
3806+ let start_col = if start_row < lines.len() {
3807+ rng.random_range(0..=lines[start_row].len())
3808+ } else {
3809+ 0
3810+ };
3811+ let end_col = if end_row < lines.len() {
3812+ rng.random_range(0..=lines[end_row].len())
3813+ } else {
3814+ 0
3815+ };
41873816
4188− let new_buffer_text = "
4189− aaa
4190− NEW
4191− ccc
4192− "
4193− .unindent();
4194− let annotated_base = "
4195− <0>aaa
4196− <1<2bbb
4197− 1>2>ccc
4198− <3>"
4199− .unindent();
4200− assert_rows_to_base_text_rows_visual(
4201− &buffer,
4202− &diff,
4203− &new_buffer_text,
4204− &annotated_base,
4205− cx,
4206− );
4207− }
3817+ let start_offset = buffer
3818+ .point_to_offset(Point::new(start_row as u32, start_col as u32))
3819+ .min(buffer.len());
3820+ let end_offset = buffer
3821+ .point_to_offset(Point::new(end_row as u32, end_col as u32))
3822+ .min(buffer.len());
42083823
4209− {
4210− let base_text = "
4211− aaa
4212− bbb
4213− ccc
4214− "
4215− .unindent();
4216− let buffer_text = "
4217− aaa
4218− ccc
4219− "
4220− .unindent();
4221− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3824+ let (start, end) = if start_offset <= end_offset {
3825+ (start_offset, end_offset)
3826+ } else {
3827+ (end_offset, start_offset)
3828+ };
42223829
4223− buffer.update(cx, |buffer, cx| {
4224− buffer.edit([(4..4, "NEW\n")], None, cx);
4225− });
3830+ let new_text = if rng.random_bool(0.3) {
3831+ String::new()
3832+ } else {
3833+ let line_count = rng.random_range(0..=2);
3834+ gen_text(rng, line_count)
3835+ };
42263836
4227− let annotated_buffer = "
4228− <0>aaa
4229− <1<2NEW
4230− 1>2>ccc
4231− <3>"
4232− .unindent();
4233− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3837+ buffer.edit([(start..end, new_text)]);
3838+ }
42343839 }
42353840
4236− {
4237− let base_text = "
4238− aaa
4239− bbb
4240− ccc
4241− ddd
4242− "
4243− .unindent();
4244− let buffer_text = "
4245− aaa
4246− ddd
4247− "
4248− .unindent();
4249− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3841+ let buffer_snapshot = buffer.snapshot();
42503842
4251− buffer.update(cx, |buffer, cx| {
4252− buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
4253− });
3843+ let buffer_text = buffer_snapshot.text();
3844+ let buffer_lines: Vec<&str> = buffer_text.lines().collect();
3845+ let base_lines: Vec<&str> = base_text.lines().collect();
42543846
4255− let new_buffer_text = "
4256− aaa
4257− XXX
4258− YYY
4259− ddd
4260− "
4261− .unindent();
4262− let annotated_base = "
4263− <0>aaa
4264− <1<2<3bbb
4265− ccc
4266− 1>2>3>ddd
4267− <4>"
4268− .unindent();
4269− assert_rows_to_base_text_rows_visual(
4270− &buffer,
4271− &diff,
4272− &new_buffer_text,
4273− &annotated_base,
4274− cx,
4275− );
4276− }
3847+ let test_count = 10;
3848+ for _ in 0..test_count {
3849+ let range = random_range_in_text(rng, &buffer_lines);
3850+ let points = points_in_range(&range, &buffer_lines);
42773851
4278− {
4279− let base_text = "
4280− aaa
4281− bbb
4282− ccc
4283− ddd
4284− "
4285− .unindent();
4286− let buffer_text = "
4287− aaa
4288− ddd
4289− "
4290− .unindent();
4291− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3852+ let optimized_patch = diff.patch_for_buffer_range(range.clone(), &buffer_snapshot);
3853+ let naive_patch = diff.patch_for_buffer_range_naive(&buffer_snapshot);
42923854
4293− buffer.update(cx, |buffer, cx| {
4294− buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
4295− });
3855+ for point in points {
3856+ let optimized_edit = optimized_patch.edit_for_old_position(point);
3857+ let naive_edit = naive_patch.edit_for_old_position(point);
42963858
4297− let annotated_buffer = "
4298− <0>aaa
4299− <1<2<3XXX
4300− YYY
4301− 1>2>3>ddd
4302− <4>"
4303− .unindent();
4304− assert_base_text_rows_to_rows_visual(&buffer, &diff, &base_text, &annotated_buffer, cx);
3859+ assert_eq!(
3860+ optimized_edit,
3861+ naive_edit,
3862+ "patch_for_buffer_range mismatch at point {:?} in range {:?}\nbase_text: {:?}\ninitial_buffer: {:?}\ncurrent_buffer: {:?}",
3863+ point,
3864+ range,
3865+ base_text,
3866+ initial_buffer_text,
3867+ buffer_snapshot.text()
3868+ );
3869+ }
43053870 }
43063871
4307− {
4308− let base_text = "
4309− aaa
4310− bbb
4311− ccc
4312− "
4313− .unindent();
4314− let buffer_text = "
4315− aaa
4316− XXXX
4317− ccc
4318− "
4319− .unindent();
4320− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
3872+ for _ in 0..test_count {
3873+ let range = random_range_in_text(rng, &base_lines);
3874+ let points = points_in_range(&range, &base_lines);
43213875
4322− buffer.update(cx, |buffer, cx| {
4323− buffer.edit([(2..10, "YY\nZZ")], None, cx);
4324− });
3876+ let optimized_patch = diff.patch_for_base_text_range(range.clone(), &buffer_snapshot);
3877+ let naive_patch = diff.patch_for_base_text_range_naive(&buffer_snapshot);
43253878
4326− let new_buffer_text = "
4327− aaYY
4328− ZZcc
4329− "
4330− .unindent();
4331− let annotated_base = "
4332− <0>aa<1a
4333− bbb
4334− c1>cc
4335− <2>"
4336− .unindent();
4337− assert_rows_to_base_text_rows_visual(
4338− &buffer,
4339− &diff,
4340− &new_buffer_text,
4341− &annotated_base,
4342− cx,
4343− );
4344− }
4345−
4346− {
4347− let base_text = "
4348− aaa
4349− bbb
4350− ccc
4351− "
4352− .unindent();
4353− let buffer_text = "
4354− aaa
4355− XXXX
4356− ccc
4357− "
4358− .unindent();
4359− let (buffer, diff) = make_diff(&base_text, &buffer_text, cx);
4360−
4361− buffer.update(cx, |buffer, cx| {
4362− buffer.edit([(0..9, "ZZ\n")], None, cx);
4363− });
3879+ for point in points {
3880+ let optimized_edit = optimized_patch.edit_for_old_position(point);
3881+ let naive_edit = naive_patch.edit_for_old_position(point);
43643882
4365− let new_buffer_text = "
4366− ZZ
4367− ccc
4368− "
4369− .unindent();
4370− let annotated_base = "
4371− <0<1aaa
4372− bbb
4373− 0>1>ccc
4374− <2>"
4375− .unindent();
4376− assert_rows_to_base_text_rows_visual(
4377− &buffer,
4378− &diff,
4379− &new_buffer_text,
4380− &annotated_base,
4381− cx,
4382− );
3883+ assert_eq!(
3884+ optimized_edit,
3885+ naive_edit,
3886+ "patch_for_base_text_range mismatch at point {:?} in range {:?}\nbase_text: {:?}\ninitial_buffer: {:?}\ncurrent_buffer: {:?}",
3887+ point,
3888+ range,
3889+ base_text,
3890+ initial_buffer_text,
3891+ buffer_snapshot.text()
3892+ );
3893+ }
43833894 }
43843895 }
4385−
4386− #[gpui::test]
4387− async fn test_row_translation_no_base_text(cx: &mut gpui::TestAppContext) {
4388− let buffer_text = "aaa\nbbb\nccc\n";
4389− let buffer = cx.new(|cx| language::Buffer::local(buffer_text, cx));
4390− let diff = cx.new(|cx| BufferDiff::new(&buffer.read(cx).text_snapshot(), cx));
4391−
4392− let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
4393− let diff_snapshot = diff.update(cx, |diff, cx| diff.snapshot(cx));
4394−
4395− let points = vec![
4396− Point::new(0, 0),
4397− Point::new(1, 0),
4398− Point::new(2, 0),
4399− Point::new(3, 0),
4400− ];
4401− let base_rows: Vec<_> = diff_snapshot
4402− .points_to_base_text_points(points, &buffer_snapshot)
4403− .0
4404− .collect();
4405−
4406− let zero = Point::new(0, 0);
4407− assert_eq!(
4408− base_rows,
4409− vec![zero..zero, zero..zero, zero..zero, zero..zero],
4410− "all buffer rows should map to point 0,0 in empty base text"
4411− );
4412−
4413− let base_points = vec![Point::new(0, 0)];
4414− let (rows_iter, _, _) =
4415− diff_snapshot.base_text_points_to_points(base_points, &buffer_snapshot);
4416− let buffer_rows: Vec<_> = rows_iter.collect();
4417−
4418− let max_point = buffer_snapshot.max_point();
4419− assert_eq!(
4420− buffer_rows,
4421− vec![zero..max_point],
4422− "base text row 0 should map to entire buffer range"
4423− );
4424− }
44253896 }

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t