History

Merge branch 'main' into modeline

Conrad Irwin committed 9 months ago

Commit af834782 parents 8d6d510 8273e6d675 files · +80785 −27108Browse files

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

Unified · Split

crates/buffer_diff/src/buffer_diff.rs+1974−228
@@ -1,28 +1,52 @@
1use futures::channel::oneshot;1use futures::channel::oneshot;
2use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};2use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
3use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task, TaskLabel};3use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
4use language::{4use language::{
5 BufferRow, Capability, DiffOptions, Language, LanguageName, LanguageRegistry,5 Capability, Diff, DiffOptions, File, Language, LanguageName, LanguageRegistry,
6 language_settings::LanguageSettings, word_diff_ranges,6 language_settings::LanguageSettings, word_diff_ranges,
7};7};
8use rope::Rope;8use rope::Rope;
9use std::{9use std::{cmp::Ordering, future::Future, iter, ops::Range, sync::Arc};
10 cmp::Ordering,
11 iter,
12 ops::Range,
13 sync::{Arc, LazyLock},
14};
15use sum_tree::SumTree;10use sum_tree::SumTree;
16use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point, ToOffset as _, ToPoint as _};11use text::{
12 Anchor, Bias, BufferId, Edit, OffsetRangeExt, Patch, Point, ToOffset as _, ToPoint as _,
13};
17use util::ResultExt;14use util::ResultExt;
18 15
19pub static CALCULATE_DIFF_TASK: LazyLock<TaskLabel> = LazyLock::new(TaskLabel::new);16fn 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
20pub const MAX_WORD_DIFF_LINE_COUNT: usize = 5;45pub const MAX_WORD_DIFF_LINE_COUNT: usize = 5;
21 46
22pub struct BufferDiff {47pub struct BufferDiff {
23 pub buffer_id: BufferId,48 pub buffer_id: BufferId,
24 inner: BufferDiffInner<Entity<language::Buffer>>,49 inner: BufferDiffInner<Entity<language::Buffer>>,
25 // diff of the index vs head
26 secondary_diff: Option<Entity<BufferDiff>>,50 secondary_diff: Option<Entity<BufferDiff>>,
27}51}
28 52
@@ -43,8 +67,10 @@ impl std::fmt::Debug for BufferDiffSnapshot { · 14 unmodified lines
43 67
44#[derive(Clone)]68#[derive(Clone)]
45pub struct BufferDiffUpdate {69pub struct BufferDiffUpdate {
46 base_text_changed: bool,
47 inner: BufferDiffInner<Arc<str>>,70 inner: BufferDiffInner<Arc<str>>,
71 buffer_snapshot: text::BufferSnapshot,
72 base_text_edits: Option<Diff>,
73 base_text_changed: bool,
48}74}
49 75
50#[derive(Clone)]76#[derive(Clone)]
@@ -53,6 +79,13 @@ struct BufferDiffInner<BaseText> { · 2 unmodified lines
53 pending_hunks: SumTree<PendingHunk>,79 pending_hunks: SumTree<PendingHunk>,
54 base_text: BaseText,80 base_text: BaseText,
55 base_text_exists: bool,81 base_text_exists: bool,
82 buffer_snapshot: text::BufferSnapshot,
83}
84
85impl<BaseText> BufferDiffInner<BaseText> {
86 fn buffer_version(&self) -> &clock::Global {
87 self.buffer_snapshot.version()
88 }
56}89}
57 90
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -188,6 +221,18 @@ impl sum_tree::SeekTarget<'_, DiffHunkSummary, DiffHunkSummary> for Anchor { · 129 unmodified lines
188 }221 }
189}222}
190 223
224impl sum_tree::SeekTarget<'_, DiffHunkSummary, DiffHunkSummary> for usize {
225 fn cmp(&self, cursor_location: &DiffHunkSummary, _cx: &text::BufferSnapshot) -> Ordering {
226 if *self < cursor_location.diff_base_byte_range.start {
227 Ordering::Less
228 } else if *self > cursor_location.diff_base_byte_range.end {
229 Ordering::Greater
230 } else {
231 Ordering::Equal
232 }
233 }
234}
235
191impl std::fmt::Debug for BufferDiffInner<language::BufferSnapshot> {236impl std::fmt::Debug for BufferDiffInner<language::BufferSnapshot> {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 f.debug_struct("BufferDiffSnapshot")238 f.debug_struct("BufferDiffSnapshot")
@@ -222,6 +267,15 @@ impl BufferDiffSnapshot { · 28 unmodified lines
222 self.secondary_diff.as_deref()267 self.secondary_diff.as_deref()
223 }268 }
224 269
270 pub fn buffer_version(&self) -> &clock::Global {
271 self.inner.buffer_version()
272 }
273
274 pub fn original_buffer_snapshot(&self) -> &text::BufferSnapshot {
275 &self.inner.buffer_snapshot
276 }
277
278 #[ztracing::instrument(skip_all)]
225 pub fn hunks_intersecting_range<'a>(279 pub fn hunks_intersecting_range<'a>(
226 &'a self,280 &'a self,
227 range: Range<Anchor>,281 range: Range<Anchor>,
@@ -331,63 +385,182 @@ impl BufferDiffSnapshot { · 103 unmodified lines
331 (new_id == old_id && new_version == old_version) || (new_empty && old_empty)385 (new_id == old_id && new_version == old_version) || (new_empty && old_empty)
332 }386 }
333 387
334 pub fn row_to_base_text_row(388 #[allow(unused)]
389 fn hunk_before_base_text_offset<'a>(
335 &self,390 &self,
336 row: BufferRow,391 target: usize,
337 bias: Bias,392 cursor: &mut sum_tree::Cursor<'a, '_, InternalDiffHunk, DiffHunkSummary>,
338 buffer: &text::BufferSnapshot,393 ) -> Option<&'a InternalDiffHunk> {
339 ) -> u32 {394 cursor.seek_forward(&target, Bias::Left);
340 // TODO(split-diff) expose a parameter to reuse a cursor to avoid repeatedly seeking from the start
341 let target = buffer.anchor_before(Point::new(row, 0));
342 // Find the last hunk that starts before the target.
343 let mut cursor = self.inner.hunks.cursor::<DiffHunkSummary>(buffer);
344 cursor.seek(&target, Bias::Left);
345 if cursor395 if cursor
346 .item()396 .item()
347 .is_none_or(|hunk| hunk.buffer_range.start.cmp(&target, buffer).is_gt())397 .is_none_or(|hunk| target < hunk.diff_base_byte_range.start)
348 {398 {
349 cursor.prev();399 cursor.prev();
350 }400 }
401 let result = cursor
402 .item()
403 .filter(|hunk| target >= hunk.diff_base_byte_range.start);
404 if cursor.item().is_none() {
405 cursor.reset();
406 }
407 result
408 }
351 409
352 let unclipped_point = if let Some(hunk) = cursor.item()410 #[allow(unused)]
353 && hunk.buffer_range.start.cmp(&target, buffer).is_le()411 fn hunk_before_buffer_anchor<'a>(
412 &self,
413 target: Anchor,
414 cursor: &mut sum_tree::Cursor<'a, '_, InternalDiffHunk, DiffHunkSummary>,
415 buffer: &text::BufferSnapshot,
416 ) -> Option<&'a InternalDiffHunk> {
417 cursor.seek_forward(&target, Bias::Left);
418 if cursor
419 .item()
420 .is_none_or(|hunk| target.cmp(&hunk.buffer_range.start, buffer).is_lt())
354 {421 {
355 // Found a hunk that starts before the target.422 cursor.prev();
356 let hunk_base_text_end = cursor.end().diff_base_byte_range.end;423 }
357 let unclipped_point = if target.cmp(&cursor.end().buffer_range.end, buffer).is_ge() {424 let result = cursor
358 // Target falls strictly between two hunks.425 .item()
359 let mut unclipped_point = hunk_base_text_end.to_point(self.base_text());426 .filter(|hunk| target.cmp(&hunk.buffer_range.start, buffer).is_ge());
360 unclipped_point +=427 if cursor.item().is_none() {
361 Point::new(row, 0) - cursor.end().buffer_range.end.to_point(buffer);428 cursor.reset();
362 unclipped_point429 }
363 } else if bias == Bias::Right {430 result
364 hunk_base_text_end.to_point(self.base_text())431 }
432
433 pub fn points_to_base_text_points<'a>(
434 &'a self,
435 points: impl IntoIterator<Item = Point> + 'a,
436 buffer: &'a text::BufferSnapshot,
437 ) -> (
438 impl 'a + Iterator<Item = Range<Point>>,
439 Option<Range<Point>>,
440 Option<(Point, Range<Point>)>,
441 ) {
442 let original_snapshot = self.original_buffer_snapshot();
443
444 let edits_since: Vec<Edit<Point>> = buffer
445 .edits_since::<Point>(original_snapshot.version())
446 .collect();
447 let mut inverted_edits_since = Patch::new(edits_since);
448 inverted_edits_since.invert();
449
450 let composed = inverted_edits_since.compose(
451 self.inner
452 .hunks
453 .iter()
454 .map(|hunk| {
455 let old_start = hunk.buffer_range.start.to_point(original_snapshot);
456 let old_end = hunk.buffer_range.end.to_point(original_snapshot);
457 let new_start = self
458 .base_text()
459 .offset_to_point(hunk.diff_base_byte_range.start);
460 let new_end = self
461 .base_text()
462 .offset_to_point(hunk.diff_base_byte_range.end);
463 Edit {
464 old: old_start..old_end,
465 new: new_start..new_end,
466 }
467 })
468 .chain(
469 if !self.inner.base_text_exists && self.inner.hunks.is_empty() {
470 Some(Edit {
471 old: Point::zero()..original_snapshot.max_point(),
472 new: Point::zero()..Point::zero(),
473 })
474 } else {
475 None
476 },
477 ),
478 );
479
480 let mut points = points.into_iter().peekable();
481
482 let first_group = points.peek().map(|point| {
483 let (_, old_range) = translate_point_through_patch(&composed, *point);
484 old_range
485 });
486
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))
365 } else {492 } else {
366 hunk.diff_base_byte_range.start.to_point(self.base_text())493 None
367 };494 }
368 // Move the cursor so that at the next step we can clip with the start of the next hunk.495 });
369 cursor.next();
370 unclipped_point
371 } else {
372 // Target is before the added region for the first hunk.
373 debug_assert!(self.inner.hunks.first().is_none_or(|first_hunk| {
374 target.cmp(&first_hunk.buffer_range.start, buffer).is_le()
375 }));
376 Point::new(row, 0)
377 };
378 496
379 // If the target falls in the region between two hunks, we added an overshoot above.497 let iter = points.map(move |point| {
380 // There may be changes in the main buffer that are not reflected in the hunks,498 let (range, _) = translate_point_through_patch(&composed, point);
381 // so we need to ensure this overshoot keeps us in the corresponding base text region.499 range
382 let max_point = if let Some(next_hunk) = cursor.item() {500 });
383 next_hunk501
384 .diff_base_byte_range502 (iter, first_group, prev_boundary)
385 .start503 }
386 .to_point(self.base_text())504
387 } else {505 pub fn base_text_points_to_points<'a>(
388 self.base_text().max_point()506 &'a self,
389 };507 points: impl IntoIterator<Item = Point> + 'a,
390 unclipped_point.min(max_point).row508 buffer: &'a text::BufferSnapshot,
509 ) -> (
510 impl 'a + Iterator<Item = Range<Point>>,
511 Option<Range<Point>>,
512 Option<(Point, Range<Point>)>,
513 ) {
514 let original_snapshot = self.original_buffer_snapshot();
515
516 let mut hunk_edits: Vec<Edit<Point>> = Vec::new();
517 for hunk in self.inner.hunks.iter() {
518 let old_start = self
519 .base_text()
520 .offset_to_point(hunk.diff_base_byte_range.start);
521 let old_end = self
522 .base_text()
523 .offset_to_point(hunk.diff_base_byte_range.end);
524 let new_start = hunk.buffer_range.start.to_point(original_snapshot);
525 let new_end = hunk.buffer_range.end.to_point(original_snapshot);
526 hunk_edits.push(Edit {
527 old: old_start..old_end,
528 new: new_start..new_end,
529 });
530 }
531 if !self.inner.base_text_exists && hunk_edits.is_empty() {
532 hunk_edits.push(Edit {
533 old: Point::zero()..Point::zero(),
534 new: Point::zero()..original_snapshot.max_point(),
535 })
536 }
537 let hunk_patch = Patch::new(hunk_edits);
538
539 let composed = hunk_patch.compose(buffer.edits_since::<Point>(original_snapshot.version()));
540
541 let mut points = points.into_iter().peekable();
542
543 let first_group = points.peek().map(|point| {
544 let (_, result) = translate_point_through_patch(&composed, *point);
545 result
546 });
547
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 });
557
558 let iter = points.map(move |point| {
559 let (range, _) = translate_point_through_patch(&composed, point);
560 range
561 });
562
563 (iter, first_group, prev_boundary)
391 }564 }
392}565}
393 566
@@ -541,7 +714,9 @@ impl BufferDiffInner<Entity<language::Buffer>> { · 147 unmodified lines
541 let next_pending_hunk_offset_range =714 let next_pending_hunk_offset_range =
542 next_pending_hunk.buffer_range.to_offset(buffer);715 next_pending_hunk.buffer_range.to_offset(buffer);
543 if next_pending_hunk_offset_range.start <= buffer_offset_range.end {716 if next_pending_hunk_offset_range.start <= buffer_offset_range.end {
544 buffer_offset_range.end = next_pending_hunk_offset_range.end;717 buffer_offset_range.end = buffer_offset_range
718 .end
719 .max(next_pending_hunk_offset_range.end);
545 pending_hunks_iter.next();720 pending_hunks_iter.next();
546 continue;721 continue;
547 }722 }
@@ -864,8 +1039,9 @@ fn compute_hunks( · 316 unmodified lines
864fn compare_hunks(1039fn compare_hunks(
865 new_hunks: &SumTree<InternalDiffHunk>,1040 new_hunks: &SumTree<InternalDiffHunk>,
866 old_hunks: &SumTree<InternalDiffHunk>,1041 old_hunks: &SumTree<InternalDiffHunk>,
1042 old_snapshot: &text::BufferSnapshot,
867 new_snapshot: &text::BufferSnapshot,1043 new_snapshot: &text::BufferSnapshot,
868) -> (Option<Range<Anchor>>, Option<Range<usize>>) {1044) -> DiffChanged {
869 let mut new_cursor = new_hunks.cursor::<()>(new_snapshot);1045 let mut new_cursor = new_hunks.cursor::<()>(new_snapshot);
870 let mut old_cursor = old_hunks.cursor::<()>(new_snapshot);1046 let mut old_cursor = old_hunks.cursor::<()>(new_snapshot);
871 old_cursor.next();1047 old_cursor.next();
@@ -875,6 +1051,10 @@ fn compare_hunks( · 3 unmodified lines
875 let mut base_text_start = None;1051 let mut base_text_start = None;
876 let mut base_text_end = None;1052 let mut base_text_end = None;
877 1053
1054 let mut last_unchanged_new_hunk_end: Option<text::Anchor> = None;
1055 let mut has_changes = false;
1056 let mut extended_end_candidate: Option<text::Anchor> = None;
1057
878 loop {1058 loop {
879 match (new_cursor.item(), old_cursor.item()) {1059 match (new_cursor.item(), old_cursor.item()) {
880 (Some(new_hunk), Some(old_hunk)) => {1060 (Some(new_hunk), Some(old_hunk)) => {
@@ -884,6 +1064,8 @@ fn compare_hunks( · 3 unmodified lines
884 .cmp(&old_hunk.buffer_range.start, new_snapshot)1064 .cmp(&old_hunk.buffer_range.start, new_snapshot)
885 {1065 {
886 Ordering::Less => {1066 Ordering::Less => {
1067 has_changes = true;
1068 extended_end_candidate = None;
887 start.get_or_insert(new_hunk.buffer_range.start);1069 start.get_or_insert(new_hunk.buffer_range.start);
888 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);1070 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
889 end.replace(new_hunk.buffer_range.end);1071 end.replace(new_hunk.buffer_range.end);
@@ -892,6 +1074,8 @@ fn compare_hunks( · 2 unmodified lines
892 }1074 }
893 Ordering::Equal => {1075 Ordering::Equal => {
894 if new_hunk != old_hunk {1076 if new_hunk != old_hunk {
1077 has_changes = true;
1078 extended_end_candidate = None;
895 start.get_or_insert(new_hunk.buffer_range.start);1079 start.get_or_insert(new_hunk.buffer_range.start);
896 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);1080 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
897 if old_hunk1081 if old_hunk
@@ -911,12 +1095,20 @@ fn compare_hunks( · 13 unmodified lines
911 .end1095 .end
912 .max(new_hunk.diff_base_byte_range.end),1096 .max(new_hunk.diff_base_byte_range.end),
913 );1097 );
1098 } else {
1099 if !has_changes {
1100 last_unchanged_new_hunk_end = Some(new_hunk.buffer_range.end);
1101 } else if extended_end_candidate.is_none() {
1102 extended_end_candidate = Some(new_hunk.buffer_range.start);
1103 }
914 }1104 }
915 1105
916 new_cursor.next();1106 new_cursor.next();
917 old_cursor.next();1107 old_cursor.next();
918 }1108 }
919 Ordering::Greater => {1109 Ordering::Greater => {
1110 has_changes = true;
1111 extended_end_candidate = None;
920 start.get_or_insert(old_hunk.buffer_range.start);1112 start.get_or_insert(old_hunk.buffer_range.start);
921 base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);1113 base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
922 end.replace(old_hunk.buffer_range.end);1114 end.replace(old_hunk.buffer_range.end);
@@ -926,18 +1118,26 @@ fn compare_hunks( · 3 unmodified lines
926 }1118 }
927 }1119 }
928 (Some(new_hunk), None) => {1120 (Some(new_hunk), None) => {
1121 has_changes = true;
1122 extended_end_candidate = None;
929 start.get_or_insert(new_hunk.buffer_range.start);1123 start.get_or_insert(new_hunk.buffer_range.start);
930 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);1124 base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
931 // TODO(cole) it seems like this could move end backward?1125 if end.is_none_or(|end| end.cmp(&new_hunk.buffer_range.end, &new_snapshot).is_le())
932 end.replace(new_hunk.buffer_range.end);1126 {
1127 end.replace(new_hunk.buffer_range.end);
1128 }
933 base_text_end = base_text_end.max(Some(new_hunk.diff_base_byte_range.end));1129 base_text_end = base_text_end.max(Some(new_hunk.diff_base_byte_range.end));
934 new_cursor.next();1130 new_cursor.next();
935 }1131 }
936 (None, Some(old_hunk)) => {1132 (None, Some(old_hunk)) => {
1133 has_changes = true;
1134 extended_end_candidate = None;
937 start.get_or_insert(old_hunk.buffer_range.start);1135 start.get_or_insert(old_hunk.buffer_range.start);
938 base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);1136 base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
939 // TODO(cole) it seems like this could move end backward?1137 if end.is_none_or(|end| end.cmp(&old_hunk.buffer_range.end, &new_snapshot).is_le())
940 end.replace(old_hunk.buffer_range.end);1138 {
1139 end.replace(old_hunk.buffer_range.end);
1140 }
941 base_text_end = base_text_end.max(Some(old_hunk.diff_base_byte_range.end));1141 base_text_end = base_text_end.max(Some(old_hunk.diff_base_byte_range.end));
942 old_cursor.next();1142 old_cursor.next();
943 }1143 }
@@ -945,12 +1145,46 @@ fn compare_hunks( · 1 unmodified line
945 }1145 }
946 }1146 }
947 1147
948 (1148 let changed_range = start.zip(end).map(|(start, end)| start..end);
949 start.zip(end).map(|(start, end)| start..end),1149 let base_text_changed_range = base_text_start
950 base_text_start1150 .zip(base_text_end)
951 .zip(base_text_end)1151 .map(|(start, end)| start..end);
952 .map(|(start, end)| start..end),1152
953 )1153 let extended_range = if has_changes && let Some(changed_range) = changed_range.clone() {
1154 let extended_start = *last_unchanged_new_hunk_end
1155 .unwrap_or(text::Anchor::min_for_buffer(new_snapshot.remote_id()))
1156 .min(&changed_range.start, new_snapshot);
1157 let extended_start = new_snapshot
1158 .anchored_edits_since_in_range::<usize>(
1159 &old_snapshot.version(),
1160 extended_start..changed_range.start,
1161 )
1162 .map(|(_, anchors)| anchors.start)
1163 .min_by(|a, b| a.cmp(b, new_snapshot))
1164 .unwrap_or(changed_range.start);
1165
1166 let extended_end = *extended_end_candidate
1167 .unwrap_or(text::Anchor::max_for_buffer(new_snapshot.remote_id()))
1168 .max(&changed_range.end, new_snapshot);
1169 let extended_end = new_snapshot
1170 .anchored_edits_since_in_range::<usize>(
1171 &old_snapshot.version(),
1172 changed_range.end..extended_end,
1173 )
1174 .map(|(_, anchors)| anchors.end)
1175 .max_by(|a, b| a.cmp(b, new_snapshot))
1176 .unwrap_or(changed_range.end);
1177
1178 Some(extended_start..extended_end)
1179 } else {
1180 None
1181 };
1182
1183 DiffChanged {
1184 changed_range,
1185 base_text_changed_range,
1186 extended_range,
1187 }
954}1188}
955 1189
956fn process_patch_hunk(1190fn process_patch_hunk(
@@ -1078,12 +1312,16 @@ impl std::fmt::Debug for BufferDiff { · 121 unmodified lines
1078 }1312 }
1079}1313}
1080 1314
1315#[derive(Clone, Debug, Default)]
1316pub struct DiffChanged {
1317 pub changed_range: Option<Range<text::Anchor>>,
1318 pub base_text_changed_range: Option<Range<usize>>,
1319 pub extended_range: Option<Range<text::Anchor>>,
1320}
1321
1081#[derive(Clone, Debug)]1322#[derive(Clone, Debug)]
1082pub enum BufferDiffEvent {1323pub enum BufferDiffEvent {
1083 DiffChanged {1324 DiffChanged(DiffChanged),
1084 changed_range: Option<Range<text::Anchor>>,
1085 base_text_changed_range: Option<Range<usize>>,
1086 },
1087 LanguageChanged,1325 LanguageChanged,
1088 HunksStagedOrUnstaged(Option<Rope>),1326 HunksStagedOrUnstaged(Option<Rope>),
1089}1327}
@@ -1105,6 +1343,7 @@ impl BufferDiff { · 15 unmodified lines
1105 hunks: SumTree::new(buffer),1343 hunks: SumTree::new(buffer),
1106 pending_hunks: SumTree::new(buffer),1344 pending_hunks: SumTree::new(buffer),
1107 base_text_exists: false,1345 base_text_exists: false,
1346 buffer_snapshot: buffer.clone(),
1108 },1347 },
1109 secondary_diff: None,1348 secondary_diff: None,
1110 }1349 }
@@ -1125,6 +1364,7 @@ impl BufferDiff { · 14 unmodified lines
1125 hunks: SumTree::new(buffer),1364 hunks: SumTree::new(buffer),
1126 pending_hunks: SumTree::new(buffer),1365 pending_hunks: SumTree::new(buffer),
1127 base_text_exists: true,1366 base_text_exists: true,
1367 buffer_snapshot: buffer.clone(),
1128 },1368 },
1129 secondary_diff: None,1369 secondary_diff: None,
1130 }1370 }
@@ -1137,13 +1377,12 @@ impl BufferDiff { · 6 unmodified lines
1137 cx: &mut Context<Self>,1377 cx: &mut Context<Self>,
1138 ) -> Self {1378 ) -> Self {
1139 let mut this = BufferDiff::new(&buffer, cx);1379 let mut this = BufferDiff::new(&buffer, cx);
1140 let executor = cx.background_executor().clone();
1141 let mut base_text = base_text.to_owned();1380 let mut base_text = base_text.to_owned();
1142 text::LineEnding::normalize(&mut base_text);1381 text::LineEnding::normalize(&mut base_text);
1143 let inner = executor.block(this.update_diff(1382 let inner = cx.foreground_executor().block_on(this.update_diff(
1144 buffer.clone(),1383 buffer.clone(),
1145 Some(Arc::from(base_text)),1384 Some(Arc::from(base_text)),
1146 true,1385 Some(false),
1147 None,1386 None,
1148 cx,1387 cx,
1149 ));1388 ));
@@ -1165,10 +1404,13 @@ impl BufferDiff { · 15 unmodified lines
1165 buffer_range: Anchor::min_min_range_for_buffer(self.buffer_id),1404 buffer_range: Anchor::min_min_range_for_buffer(self.buffer_id),
1166 diff_base_byte_range: 0..0,1405 diff_base_byte_range: 0..0,
1167 });1406 });
1168 cx.emit(BufferDiffEvent::DiffChanged {1407 let changed_range = Some(Anchor::min_max_range_for_buffer(self.buffer_id));
1169 changed_range: Some(Anchor::min_max_range_for_buffer(self.buffer_id)),1408 let base_text_range = Some(0..self.base_text(cx).len());
1170 base_text_changed_range: Some(0..self.base_text(cx).len()),1409 cx.emit(BufferDiffEvent::DiffChanged(DiffChanged {
1171 });1410 changed_range: changed_range.clone(),
1411 base_text_changed_range: base_text_range,
1412 extended_range: changed_range,
1413 }));
1172 }1414 }
1173 }1415 }
1174 1416
@@ -1198,13 +1440,14 @@ impl BufferDiff { · 23 unmodified lines
1198 new_index_text.clone(),1440 new_index_text.clone(),
1199 ));1441 ));
1200 if let Some((first, last)) = hunks.first().zip(hunks.last()) {1442 if let Some((first, last)) = hunks.first().zip(hunks.last()) {
1201 let changed_range = first.buffer_range.start..last.buffer_range.end;1443 let changed_range = Some(first.buffer_range.start..last.buffer_range.end);
1202 let base_text_changed_range =1444 let base_text_changed_range =
1203 first.diff_base_byte_range.start..last.diff_base_byte_range.end;1445 Some(first.diff_base_byte_range.start..last.diff_base_byte_range.end);
1204 cx.emit(BufferDiffEvent::DiffChanged {1446 cx.emit(BufferDiffEvent::DiffChanged(DiffChanged {
1205 changed_range: Some(changed_range),1447 changed_range: changed_range.clone(),
1206 base_text_changed_range: Some(base_text_changed_range),1448 base_text_changed_range,
1207 });1449 extended_range: changed_range,
1450 }));
1208 }1451 }
1209 new_index_text1452 new_index_text
1210 }1453 }
@@ -1227,13 +1470,14 @@ impl BufferDiff { · 16 unmodified lines
1227 self.inner1470 self.inner
1228 .stage_or_unstage_hunks_impl(&secondary, stage, &hunks, buffer, file_exists, cx);1471 .stage_or_unstage_hunks_impl(&secondary, stage, &hunks, buffer, file_exists, cx);
1229 if let Some((first, last)) = hunks.first().zip(hunks.last()) {1472 if let Some((first, last)) = hunks.first().zip(hunks.last()) {
1230 let changed_range = first.buffer_range.start..last.buffer_range.end;1473 let changed_range = Some(first.buffer_range.start..last.buffer_range.end);
1231 let base_text_changed_range =1474 let base_text_changed_range =
1232 first.diff_base_byte_range.start..last.diff_base_byte_range.end;1475 Some(first.diff_base_byte_range.start..last.diff_base_byte_range.end);
1233 cx.emit(BufferDiffEvent::DiffChanged {1476 cx.emit(BufferDiffEvent::DiffChanged(DiffChanged {
1234 changed_range: Some(changed_range),1477 changed_range: changed_range.clone(),
1235 base_text_changed_range: Some(base_text_changed_range),1478 base_text_changed_range,
1236 });1479 extended_range: changed_range,
1480 }));
1237 }1481 }
1238 }1482 }
1239 1483
@@ -1241,19 +1485,31 @@ impl BufferDiff { · 1 unmodified line
1241 &self,1485 &self,
1242 buffer: text::BufferSnapshot,1486 buffer: text::BufferSnapshot,
1243 base_text: Option<Arc<str>>,1487 base_text: Option<Arc<str>>,
1244 base_text_changed: bool,1488 base_text_change: Option<bool>,
1245 language: Option<Arc<Language>>,1489 language: Option<Arc<Language>>,
1246 cx: &App,1490 cx: &App,
1247 ) -> Task<BufferDiffUpdate> {1491 ) -> Task<BufferDiffUpdate> {
1248 let prev_base_text = self.base_text(cx).as_rope().clone();1492 let prev_base_text = self.base_text(cx).as_rope().clone();
1493 let base_text_changed = base_text_change.is_some();
1494 let compute_base_text_edits = base_text_change == Some(true);
1249 let diff_options = build_diff_options(1495 let diff_options = build_diff_options(
1250 language.as_ref().map(|l| l.name()),1496 language.as_ref().map(|l| l.name()),
1251 language.as_ref().map(|l| l.default_scope()),1497 language.as_ref().map(|l| l.default_scope()),
1252 cx,1498 cx,
1253 );1499 );
1500 let buffer_snapshot = buffer.clone();
1501
1502 let base_text_diff_task = if base_text_changed && compute_base_text_edits {
1503 base_text
1504 .as_ref()
1505 .map(|new_text| self.inner.base_text.read(cx).diff(new_text.clone(), cx))
1506 } else {
1507 None
1508 };
1254 1509
1255 cx.background_executor()1510 let hunk_task = cx.background_executor().spawn({
1256 .spawn_labeled(*CALCULATE_DIFF_TASK, async move {1511 let buffer_snapshot = buffer_snapshot.clone();
1512 async move {
1257 let base_text_rope = if let Some(base_text) = &base_text {1513 let base_text_rope = if let Some(base_text) = &base_text {
1258 if base_text_changed {1514 if base_text_changed {
1259 Rope::from(base_text.as_ref())1515 Rope::from(base_text.as_ref())
@@ -1272,19 +1528,35 @@ impl BufferDiff { · 12 unmodified lines
1272 diff_options,1528 diff_options,
1273 );1529 );
1274 let base_text = base_text.unwrap_or_default();1530 let base_text = base_text.unwrap_or_default();
1275 let inner = BufferDiffInner {1531 BufferDiffInner {
1276 base_text,1532 base_text,
1277 hunks,1533 hunks,
1278 base_text_exists,1534 base_text_exists,
1279 pending_hunks: SumTree::new(&buffer),1535 pending_hunks: SumTree::new(&buffer),
1280 };1536 buffer_snapshot,
1281 BufferDiffUpdate {
1282 inner,
1283 base_text_changed,
1284 }1537 }
1285 })1538 }
1539 });
1540
1541 cx.background_executor().spawn(async move {
1542 let (inner, base_text_edits) = match base_text_diff_task {
1543 Some(diff_task) => {
1544 let (inner, diff) = futures::join!(hunk_task, diff_task);
1545 (inner, Some(diff))
1546 }
1547 None => (hunk_task.await, None),
1548 };
1549
1550 BufferDiffUpdate {
1551 inner,
1552 buffer_snapshot,
1553 base_text_edits,
1554 base_text_changed,
1555 }
1556 })
1286 }1557 }
1287 1558
1559 #[ztracing::instrument(skip_all)]
1288 pub fn language_changed(1560 pub fn language_changed(
1289 &mut self,1561 &mut self,
1290 language: Option<Arc<Language>>,1562 language: Option<Arc<Language>>,
@@ -1315,23 +1587,37 @@ impl BufferDiff { · 24 unmodified lines
1315 secondary_diff_change: Option<Range<Anchor>>,1587 secondary_diff_change: Option<Range<Anchor>>,
1316 clear_pending_hunks: bool,1588 clear_pending_hunks: bool,
1317 cx: &mut Context<Self>,1589 cx: &mut Context<Self>,
1318 ) -> impl Future<Output = (Option<Range<Anchor>>, Option<Range<usize>>)> + use<> {1590 ) -> impl Future<Output = DiffChanged> + use<> {
1319 log::debug!("set snapshot with secondary {secondary_diff_change:?}");1591 log::debug!("set snapshot with secondary {secondary_diff_change:?}");
1320 1592
1321 let old_snapshot = self.snapshot(cx);1593 let old_snapshot = self.snapshot(cx);
1322 let state = &mut self.inner;1594 let state = &mut self.inner;
1323 let new_state = update.inner;1595 let new_state = update.inner;
1324 let (mut changed_range, mut base_text_changed_range) =1596 let base_text_changed = update.base_text_changed;
1325 match (state.base_text_exists, new_state.base_text_exists) {1597
1326 (false, false) => (None, None),1598 let old_buffer_snapshot = &old_snapshot.inner.buffer_snapshot;
1327 (true, true) if !update.base_text_changed => {1599 let DiffChanged {
1328 compare_hunks(&new_state.hunks, &old_snapshot.inner.hunks, buffer)1600 mut changed_range,
1601 mut base_text_changed_range,
1602 mut extended_range,
1603 } = match (state.base_text_exists, new_state.base_text_exists) {
1604 (false, false) => DiffChanged::default(),
1605 (true, true) if !base_text_changed => compare_hunks(
1606 &new_state.hunks,
1607 &old_snapshot.inner.hunks,
1608 old_buffer_snapshot,
1609 buffer,
1610 ),
1611 _ => {
1612 let full_range = text::Anchor::min_max_range_for_buffer(self.buffer_id);
1613 let full_base_range = 0..new_state.base_text.len();
1614 DiffChanged {
1615 changed_range: Some(full_range.clone()),
1616 base_text_changed_range: Some(full_base_range),
1617 extended_range: Some(full_range),
1329 }1618 }
1330 _ => (1619 }
1331 Some(text::Anchor::min_max_range_for_buffer(self.buffer_id)),1620 };
1332 Some(0..new_state.base_text.len()),
1333 ),
1334 };
1335 1621
1336 if let Some(secondary_changed_range) = secondary_diff_change1622 if let Some(secondary_changed_range) = secondary_diff_change
1337 && let (Some(secondary_hunk_range), Some(secondary_base_range)) =1623 && let (Some(secondary_hunk_range), Some(secondary_base_range)) =
@@ -1341,20 +1627,34 @@ impl BufferDiff { · 3 unmodified lines
1341 range.start = *secondary_hunk_range.start.min(&range.start, buffer);1627 range.start = *secondary_hunk_range.start.min(&range.start, buffer);
1342 range.end = *secondary_hunk_range.end.max(&range.end, buffer);1628 range.end = *secondary_hunk_range.end.max(&range.end, buffer);
1343 } else {1629 } else {
1344 changed_range = Some(secondary_hunk_range);1630 changed_range = Some(secondary_hunk_range.clone());
1345 }1631 }
1346 1632
1347 if let Some(base_text_range) = &mut base_text_changed_range {1633 if let Some(base_text_range) = base_text_changed_range.as_mut() {
1348 base_text_range.start = secondary_base_range.start.min(base_text_range.start);1634 base_text_range.start = secondary_base_range.start.min(base_text_range.start);
1349 base_text_range.end = secondary_base_range.end.max(base_text_range.end);1635 base_text_range.end = secondary_base_range.end.max(base_text_range.end);
1350 } else {1636 } else {
1351 base_text_changed_range = Some(secondary_base_range);1637 base_text_changed_range = Some(secondary_base_range);
1352 }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 }
1353 }1646 }
1354 1647
1355 let state = &mut self.inner;1648 let state = &mut self.inner;
1356 state.base_text_exists = new_state.base_text_exists;1649 state.base_text_exists = new_state.base_text_exists;
1357 let parsing_idle = if update.base_text_changed {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 {
1358 state.base_text.update(cx, |base_text, cx| {1658 state.base_text.update(cx, |base_text, cx| {
1359 base_text.set_capability(Capability::ReadWrite, cx);1659 base_text.set_capability(Capability::ReadWrite, cx);
1360 base_text.set_text(new_state.base_text.clone(), cx);1660 base_text.set_text(new_state.base_text.clone(), cx);
@@ -1365,17 +1665,19 @@ impl BufferDiff { · 4 unmodified lines
1365 None1665 None
1366 };1666 };
1367 state.hunks = new_state.hunks;1667 state.hunks = new_state.hunks;
1368 if update.base_text_changed || clear_pending_hunks {1668 state.buffer_snapshot = update.buffer_snapshot;
1669 if base_text_changed || clear_pending_hunks {
1369 if let Some((first, last)) = state.pending_hunks.first().zip(state.pending_hunks.last())1670 if let Some((first, last)) = state.pending_hunks.first().zip(state.pending_hunks.last())
1370 {1671 {
1672 let pending_range = first.buffer_range.start..last.buffer_range.end;
1371 if let Some(range) = &mut changed_range {1673 if let Some(range) = &mut changed_range {
1372 range.start = *range.start.min(&first.buffer_range.start, buffer);1674 range.start = *range.start.min(&pending_range.start, buffer);
1373 range.end = *range.end.max(&last.buffer_range.end, buffer);1675 range.end = *range.end.max(&pending_range.end, buffer);
1374 } else {1676 } else {
1375 changed_range = Some(first.buffer_range.start..last.buffer_range.end);1677 changed_range = Some(pending_range.clone());
1376 }1678 }
1377 1679
1378 if let Some(base_text_range) = &mut base_text_changed_range {1680 if let Some(base_text_range) = base_text_changed_range.as_mut() {
1379 base_text_range.start =1681 base_text_range.start =
1380 base_text_range.start.min(first.diff_base_byte_range.start);1682 base_text_range.start.min(first.diff_base_byte_range.start);
1381 base_text_range.end = base_text_range.end.max(last.diff_base_byte_range.end);1683 base_text_range.end = base_text_range.end.max(last.diff_base_byte_range.end);
@@ -1383,6 +1685,13 @@ impl BufferDiff { · 1 unmodified line
1383 base_text_changed_range =1685 base_text_changed_range =
1384 Some(first.diff_base_byte_range.start..last.diff_base_byte_range.end);1686 Some(first.diff_base_byte_range.start..last.diff_base_byte_range.end);
1385 }1687 }
1688
1689 if let Some(ext) = &mut extended_range {
1690 ext.start = *ext.start.min(&pending_range.start, buffer);
1691 ext.end = *ext.end.max(&pending_range.end, buffer);
1692 } else {
1693 extended_range = Some(pending_range);
1694 }
1386 }1695 }
1387 state.pending_hunks = SumTree::new(buffer);1696 state.pending_hunks = SumTree::new(buffer);
1388 }1697 }
@@ -1391,7 +1700,11 @@ impl BufferDiff { · 2 unmodified lines
1391 if let Some(parsing_idle) = parsing_idle {1700 if let Some(parsing_idle) = parsing_idle {
1392 parsing_idle.await;1701 parsing_idle.await;
1393 }1702 }
1394 (changed_range, base_text_changed_range)1703 DiffChanged {
1704 changed_range,
1705 base_text_changed_range,
1706 extended_range,
1707 }
1395 }1708 }
1396 }1709 }
1397 1710
@@ -1421,15 +1734,12 @@ impl BufferDiff { · 23 unmodified lines
1421 );1734 );
1422 1735
1423 cx.spawn(async move |this, cx| {1736 cx.spawn(async move |this, cx| {
1424 let (changed_range, base_text_changed_range) = fut.await;1737 let change = fut.await;
1425 this.update(cx, |_, cx| {1738 this.update(cx, |_, cx| {
1426 cx.emit(BufferDiffEvent::DiffChanged {1739 cx.emit(BufferDiffEvent::DiffChanged(change.clone()));
1427 changed_range: changed_range.clone(),
1428 base_text_changed_range,
1429 });
1430 })1740 })
1431 .ok();1741 .ok();
1432 changed_range1742 change.changed_range
1433 })1743 })
1434 }1744 }
1435 1745
@@ -1448,6 +1758,7 @@ impl BufferDiff { · 12 unmodified lines
1448 pending_hunks: self.inner.pending_hunks.clone(),1758 pending_hunks: self.inner.pending_hunks.clone(),
1449 base_text: self.inner.base_text.read(cx).snapshot(),1759 base_text: self.inner.base_text.read(cx).snapshot(),
1450 base_text_exists: self.inner.base_text_exists,1760 base_text_exists: self.inner.base_text_exists,
1761 buffer_snapshot: self.inner.buffer_snapshot.clone(),
1451 },1762 },
1452 secondary_diff: self1763 secondary_diff: self
1453 .secondary_diff1764 .secondary_diff
@@ -1471,7 +1782,7 @@ impl BufferDiff { · 17 unmodified lines
1471 cx.spawn(async move |this, cx| {1782 cx.spawn(async move |this, cx| {
1472 let Some(state) = this1783 let Some(state) = this
1473 .update(cx, |this, cx| {1784 .update(cx, |this, cx| {
1474 this.update_diff(buffer.clone(), base_text, true, language, cx)1785 this.update_diff(buffer.clone(), base_text, Some(false), language, cx)
1475 })1786 })
1476 .log_err()1787 .log_err()
1477 else {1788 else {
@@ -1500,15 +1811,12 @@ impl BufferDiff { · 22 unmodified lines
1500 pub fn recalculate_diff_sync(&mut self, buffer: &text::BufferSnapshot, cx: &mut Context<Self>) {1811 pub fn recalculate_diff_sync(&mut self, buffer: &text::BufferSnapshot, cx: &mut Context<Self>) {
1501 let language = self.base_text(cx).language().cloned();1812 let language = self.base_text(cx).language().cloned();
1502 let base_text = self.base_text_string(cx).map(|s| s.as_str().into());1813 let base_text = self.base_text_string(cx).map(|s| s.as_str().into());
1503 let fut = self.update_diff(buffer.clone(), base_text, false, language, cx);1814 let fut = self.update_diff(buffer.clone(), base_text, None, language, cx);
1504 let executor = cx.background_executor().clone();1815 let fg_executor = cx.foreground_executor().clone();
1505 let snapshot = executor.block(fut);1816 let snapshot = fg_executor.block_on(fut);
1506 let fut = self.set_snapshot_with_secondary_inner(snapshot, buffer, None, false, cx);1817 let fut = self.set_snapshot_with_secondary_inner(snapshot, buffer, None, false, cx);
1507 let (changed_range, base_text_changed_range) = executor.block(fut);1818 let change = fg_executor.block_on(fut);
1508 cx.emit(BufferDiffEvent::DiffChanged {1819 cx.emit(BufferDiffEvent::DiffChanged(change));
1509 changed_range,
1510 base_text_changed_range,
1511 })
1512 }1820 }
1513 1821
1514 pub fn base_text_buffer(&self) -> Entity<language::Buffer> {1822 pub fn base_text_buffer(&self) -> Entity<language::Buffer> {
@@ -2144,6 +2452,72 @@ mod tests { · 629 unmodified lines
2144 }2452 }
2145 }2453 }
2146 2454
2455 #[gpui::test]
2456 async fn test_stage_all_with_nested_hunks(cx: &mut TestAppContext) {
2457 // This test reproduces a crash where staging all hunks would cause an underflow
2458 // when there's one large unstaged hunk containing multiple uncommitted hunks.
2459 let head_text = "
2460 aaa
2461 bbb
2462 ccc
2463 ddd
2464 eee
2465 fff
2466 ggg
2467 hhh
2468 iii
2469 jjj
2470 kkk
2471 lll
2472 "
2473 .unindent();
2474
2475 let index_text = "
2476 aaa
2477 bbb
2478 CCC-index
2479 DDD-index
2480 EEE-index
2481 FFF-index
2482 GGG-index
2483 HHH-index
2484 III-index
2485 JJJ-index
2486 kkk
2487 lll
2488 "
2489 .unindent();
2490
2491 let buffer_text = "
2492 aaa
2493 bbb
2494 ccc-modified
2495 ddd
2496 eee-modified
2497 fff
2498 ggg
2499 hhh-modified
2500 iii
2501 jjj
2502 kkk
2503 lll
2504 "
2505 .unindent();
2506
2507 let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
2508
2509 let unstaged_diff = cx.new(|cx| BufferDiff::new_with_base_text(&index_text, &buffer, cx));
2510 let uncommitted_diff = cx.new(|cx| {
2511 let mut diff = BufferDiff::new_with_base_text(&head_text, &buffer, cx);
2512 diff.set_secondary_diff(unstaged_diff);
2513 diff
2514 });
2515
2516 uncommitted_diff.update(cx, |diff, cx| {
2517 diff.stage_or_unstage_all_hunks(true, &buffer, true, cx);
2518 });
2519 }
2520
2147 #[gpui::test]2521 #[gpui::test]
2148 async fn test_toggling_stage_and_unstage_same_hunk(cx: &mut TestAppContext) {2522 async fn test_toggling_stage_and_unstage_same_hunk(cx: &mut TestAppContext) {
2149 let head_text = "2523 let head_text = "
@@ -2233,11 +2607,19 @@ mod tests { · 83 unmodified lines
2233 2607
2234 let empty_diff = cx.update(|cx| BufferDiff::new(&buffer, cx).snapshot(cx));2608 let empty_diff = cx.update(|cx| BufferDiff::new(&buffer, cx).snapshot(cx));
2235 let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2609 let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2236 let (range, base_text_range) =2610 let DiffChanged {
2237 compare_hunks(&diff_1.inner.hunks, &empty_diff.inner.hunks, &buffer);2611 changed_range,
2238 let range = range.unwrap();2612 base_text_changed_range,
2613 extended_range: _,
2614 } = compare_hunks(
2615 &diff_1.inner.hunks,
2616 &empty_diff.inner.hunks,
2617 &buffer,
2618 &buffer,
2619 );
2620 let range = changed_range.unwrap();
2239 assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));2621 assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
2240 let base_text_range = base_text_range.unwrap();2622 let base_text_range = base_text_changed_range.unwrap();
2241 assert_eq!(2623 assert_eq!(
2242 base_text_range.to_point(diff_1.base_text()),2624 base_text_range.to_point(diff_1.base_text()),
2243 Point::new(0, 0)..Point::new(10, 0)2625 Point::new(0, 0)..Point::new(10, 0)
@@ -2258,14 +2640,19 @@ mod tests { · 14 unmodified lines
2258 .unindent(),2640 .unindent(),
2259 );2641 );
2260 let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2642 let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2261 let (range, base_text_range) =2643 let DiffChanged {
2262 compare_hunks(&diff_2.inner.hunks, &diff_1.inner.hunks, &buffer);2644 changed_range,
2645 base_text_changed_range,
2646 extended_range: _,
2647 } = compare_hunks(&diff_2.inner.hunks, &diff_1.inner.hunks, &buffer, &buffer);
2263 assert_eq!(2648 assert_eq!(
2264 range.unwrap().to_point(&buffer),2649 changed_range.unwrap().to_point(&buffer),
2265 Point::new(4, 0)..Point::new(5, 0),2650 Point::new(4, 0)..Point::new(5, 0),
2266 );2651 );
2267 assert_eq!(2652 assert_eq!(
2268 base_text_range.unwrap().to_point(diff_2.base_text()),2653 base_text_changed_range
2654 .unwrap()
2655 .to_point(diff_2.base_text()),
2269 Point::new(6, 0)..Point::new(7, 0),2656 Point::new(6, 0)..Point::new(7, 0),
2270 );2657 );
2271 2658
@@ -2284,11 +2671,14 @@ mod tests { · 12 unmodified lines
2284 .unindent(),2671 .unindent(),
2285 );2672 );
2286 let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2673 let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2287 let (range, base_text_range) =2674 let DiffChanged {
2288 compare_hunks(&diff_3.inner.hunks, &diff_2.inner.hunks, &buffer);2675 changed_range,
2289 let range = range.unwrap();2676 base_text_changed_range,
2677 extended_range: _,
2678 } = compare_hunks(&diff_3.inner.hunks, &diff_2.inner.hunks, &buffer, &buffer);
2679 let range = changed_range.unwrap();
2290 assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));2680 assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));
2291 let base_text_range = base_text_range.unwrap();2681 let base_text_range = base_text_changed_range.unwrap();
2292 assert_eq!(2682 assert_eq!(
2293 base_text_range.to_point(diff_3.base_text()),2683 base_text_range.to_point(diff_3.base_text()),
2294 Point::new(2, 0)..Point::new(4, 0)2684 Point::new(2, 0)..Point::new(4, 0)
@@ -2308,11 +2698,14 @@ mod tests { · 13 unmodified lines
2308 .unindent(),2698 .unindent(),
2309 );2699 );
2310 let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2700 let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2311 let (range, base_text_range) =2701 let DiffChanged {
2312 compare_hunks(&diff_4.inner.hunks, &diff_3.inner.hunks, &buffer);2702 changed_range,
2313 let range = range.unwrap();2703 base_text_changed_range,
2704 extended_range: _,
2705 } = compare_hunks(&diff_4.inner.hunks, &diff_3.inner.hunks, &buffer, &buffer);
2706 let range = changed_range.unwrap();
2314 assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));2707 assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));
2315 let base_text_range = base_text_range.unwrap();2708 let base_text_range = base_text_changed_range.unwrap();
2316 assert_eq!(2709 assert_eq!(
2317 base_text_range.to_point(diff_4.base_text()),2710 base_text_range.to_point(diff_4.base_text()),
2318 Point::new(6, 0)..Point::new(7, 0)2711 Point::new(6, 0)..Point::new(7, 0)
@@ -2333,11 +2726,14 @@ mod tests { · 14 unmodified lines
2333 .unindent(),2726 .unindent(),
2334 );2727 );
2335 let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);2728 let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
2336 let (range, base_text_range) =2729 let DiffChanged {
2337 compare_hunks(&diff_5.inner.hunks, &diff_4.inner.hunks, &buffer);2730 changed_range,
2338 let range = range.unwrap();2731 base_text_changed_range,
2732 extended_range: _,
2733 } = compare_hunks(&diff_5.inner.hunks, &diff_4.inner.hunks, &buffer, &buffer);
2734 let range = changed_range.unwrap();
2339 assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));2735 assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));
2340 let base_text_range = base_text_range.unwrap();2736 let base_text_range = base_text_changed_range.unwrap();
2341 assert_eq!(2737 assert_eq!(
2342 base_text_range.to_point(diff_5.base_text()),2738 base_text_range.to_point(diff_5.base_text()),
2343 Point::new(5, 0)..Point::new(5, 0)2739 Point::new(5, 0)..Point::new(5, 0)
@@ -2358,11 +2754,14 @@ mod tests { · 14 unmodified lines
2358 .unindent(),2754 .unindent(),
2359 );2755 );
2360 let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);2756 let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
2361 let (range, base_text_range) =2757 let DiffChanged {
2362 compare_hunks(&diff_6.inner.hunks, &diff_5.inner.hunks, &buffer);2758 changed_range,
2363 let range = range.unwrap();2759 base_text_changed_range,
2760 extended_range: _,
2761 } = compare_hunks(&diff_6.inner.hunks, &diff_5.inner.hunks, &buffer, &buffer);
2762 let range = changed_range.unwrap();
2364 assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));2763 assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));
2365 let base_text_range = base_text_range.unwrap();2764 let base_text_range = base_text_changed_range.unwrap();
2366 assert_eq!(2765 assert_eq!(
2367 base_text_range.to_point(diff_6.base_text()),2766 base_text_range.to_point(diff_6.base_text()),
2368 Point::new(9, 0)..Point::new(10, 0)2767 Point::new(9, 0)..Point::new(10, 0)
@@ -2534,69 +2933,6 @@ mod tests { · 165 unmodified lines
2534 }2933 }
2535 }2934 }
2536 2935
2537 #[gpui::test]
2538 async fn test_row_to_base_text_row(cx: &mut TestAppContext) {
2539 let base_text = "
2540 zero
2541 one
2542 two
2543 three
2544 four
2545 five
2546 six
2547 seven
2548 eight
2549 "
2550 .unindent();
2551 let buffer_text = "
2552 zero
2553 ONE
2554 two
2555 NINE
2556 five
2557 seven
2558 "
2559 .unindent();
2560
2561 // zero
2562 // - one
2563 // + ONE
2564 // two
2565 // - three
2566 // - four
2567 // + NINE
2568 // five
2569 // - six
2570 // seven
2571 // + eight
2572
2573 let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
2574 let buffer_snapshot = buffer.snapshot();
2575 let diff = BufferDiffSnapshot::new_sync(buffer_snapshot.clone(), base_text, cx);
2576 let expected_results = [
2577 // main buffer row, base text row (right bias), base text row (left bias)
2578 (0, 0, 0),
2579 (1, 2, 1),
2580 (2, 2, 2),
2581 (3, 5, 3),
2582 (4, 5, 5),
2583 (5, 7, 7),
2584 (6, 9, 9),
2585 ];
2586 for (buffer_row, expected_right, expected_left) in expected_results {
2587 assert_eq!(
2588 diff.row_to_base_text_row(buffer_row, Bias::Right, &buffer_snapshot),
2589 expected_right,
2590 "{buffer_row}"
2591 );
2592 assert_eq!(
2593 diff.row_to_base_text_row(buffer_row, Bias::Left, &buffer_snapshot),
2594 expected_left,
2595 "{buffer_row}"
2596 );
2597 }
2598 }
2599
2600 #[gpui::test]2936 #[gpui::test]
2601 async fn test_changed_ranges(cx: &mut gpui::TestAppContext) {2937 async fn test_changed_ranges(cx: &mut gpui::TestAppContext) {
2602 let base_text = "2938 let base_text = "
@@ -2646,7 +2982,7 @@ mod tests { · 43 unmodified lines
2646 diff.update_diff(2982 diff.update_diff(
2647 snapshot.clone(),2983 snapshot.clone(),
2648 Some(base_text.as_str().into()),2984 Some(base_text.as_str().into()),
2649 false,2985 None,
2650 None,2986 None,
2651 cx,2987 cx,
2652 )2988 )
@@ -2659,10 +2995,11 @@ mod tests { · 6 unmodified lines
2659 let events = rx.into_iter().collect::<Vec<_>>();2995 let events = rx.into_iter().collect::<Vec<_>>();
2660 match events.as_slice() {2996 match events.as_slice() {
2661 [2997 [
2662 BufferDiffEvent::DiffChanged {2998 BufferDiffEvent::DiffChanged(DiffChanged {
2663 changed_range: _,2999 changed_range: _,
2664 base_text_changed_range,3000 base_text_changed_range,
2665 },3001 extended_range: _,
3002 }),
2666 ] => {3003 ] => {
2667 // TODO(cole) this seems like it should pass but currently fails (see compare_hunks)3004 // TODO(cole) this seems like it should pass but currently fails (see compare_hunks)
2668 // assert_eq!(3005 // assert_eq!(
@@ -2676,4 +3013,1413 @@ mod tests { · 7 unmodified lines
2676 _ => panic!("unexpected events: {:?}", events),3013 _ => panic!("unexpected events: {:?}", events),
2677 }3014 }
2678 }3015 }
3016
3017 #[gpui::test]
3018 async fn test_extended_range(cx: &mut TestAppContext) {
3019 let base_text = "
3020 aaa
3021 bbb
3022
3023
3024
3025
3026
3027 ccc
3028 ddd
3029 "
3030 .unindent();
3031
3032 let buffer_text = "
3033 aaa
3034 bbb
3035
3036
3037
3038
3039
3040 CCC
3041 ddd
3042 "
3043 .unindent();
3044
3045 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);
3048
3049 buffer.edit([(Point::new(1, 3)..Point::new(1, 3), "\n")]);
3050 let diff_b = BufferDiffSnapshot::new_sync(buffer.clone(), base_text, cx);
3051
3052 let DiffChanged {
3053 changed_range,
3054 base_text_changed_range: _,
3055 extended_range,
3056 } = compare_hunks(
3057 &diff_b.inner.hunks,
3058 &diff_a.inner.hunks,
3059 &old_buffer,
3060 &buffer,
3061 );
3062
3063 let changed_range = changed_range.unwrap();
3064 assert_eq!(
3065 changed_range.to_point(&buffer),
3066 Point::new(7, 0)..Point::new(9, 0),
3067 "changed_range should span from old hunk position to new hunk end"
3068 );
3069
3070 let extended_range = extended_range.unwrap();
3071 assert_eq!(
3072 extended_range.start.to_point(&buffer),
3073 Point::new(1, 3),
3074 "extended_range.start should extend to include the edit outside changed_range"
3075 );
3076 assert_eq!(
3077 extended_range.end.to_point(&buffer),
3078 Point::new(9, 0),
3079 "extended_range.end should collapse to changed_range.end when no edits in end margin"
3080 );
3081
3082 let base_text_2 = "
3083 one
3084 two
3085 three
3086 four
3087 five
3088 six
3089 seven
3090 eight
3091 "
3092 .unindent();
3093
3094 let buffer_text_2 = "
3095 ONE
3096 two
3097 THREE
3098 four
3099 FIVE
3100 six
3101 SEVEN
3102 eight
3103 "
3104 .unindent();
3105
3106 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);
3109
3110 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);
3112
3113 let DiffChanged {
3114 changed_range,
3115 base_text_changed_range: _,
3116 extended_range,
3117 } = compare_hunks(
3118 &diff_2b.inner.hunks,
3119 &diff_2a.inner.hunks,
3120 &old_buffer_2,
3121 &buffer_2,
3122 );
3123
3124 let changed_range = changed_range.unwrap();
3125 assert_eq!(
3126 changed_range.to_point(&buffer_2),
3127 Point::new(4, 0)..Point::new(5, 0),
3128 "changed_range should be just the hunk that changed (FIVE)"
3129 );
3130
3131 let extended_range = extended_range.unwrap();
3132 assert_eq!(
3133 extended_range.to_point(&buffer_2),
3134 Point::new(4, 0)..Point::new(5, 0),
3135 "extended_range should equal changed_range when edit is within the hunk"
3136 );
3137 }
3138
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));
3150
3151 assert_eq!(
3152 buffer.text(),
3153 source_text,
3154 "buffer text does not match source text"
3155 );
3156
3157 assert_eq!(
3158 diff.base_text_string().unwrap_or_default(),
3159 target_text,
3160 "base text does not match stripped annotated target"
3161 );
3162
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();
3173
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
3178 );
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);
3189
3190 let buffer = buffer.read_with(cx, |buffer, _| buffer.text_snapshot());
3191 let diff = diff.update(cx, |diff, cx| diff.snapshot(cx));
3192
3193 assert_eq!(
3194 diff.base_text_string().unwrap_or_default(),
3195 source_text,
3196 "base text does not match source text"
3197 );
3198
3199 assert_eq!(
3200 buffer.text(),
3201 target_text,
3202 "buffer text does not match stripped annotated target"
3203 );
3204
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();
3215
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
3220 );
3221 }
3222
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();
3226
3227 let mut clean_text = String::new();
3228 let mut current_point = Point::new(0, 0);
3229 let mut chars = annotated_text.chars().peekable();
3230
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);
3244
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 }
3281
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 }
3289
3290 (clean_text, ranges)
3291 }
3292
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 }
3304
3305 #[gpui::test]
3306 async fn test_row_translation_visual(cx: &mut gpui::TestAppContext) {
3307 use unindent::Unindent;
3308
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 }
3326
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 }
3344
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 }
3362
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 }
3379
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 }
3396
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 }
3405
3406 {
3407 let base_text = "
3408 aaa
3409 bbb
3410 ccc
3411 "
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 }
3423
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 }
3441
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 }
3459
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 }
3476
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 }
3493 }
3494
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 );
3897 }
3898
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 );
3920 }
3921
3922 {
3923 let base_text = "";
3924 let buffer_text = "";
3925 let (buffer, diff) = make_diff(base_text, buffer_text, cx);
3926
3927 buffer.update(cx, |buffer, cx| {
3928 buffer.edit([(0..0, "aaa\n")], None, cx);
3929 });
3930
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 }
3941
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);
3956
3957 buffer.update(cx, |buffer, cx| {
3958 buffer.edit([(4..7, "YYY")], None, cx);
3959 });
3960
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 );
3980 }
3981
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);
4008 }
4009
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 );
4048 }
4049
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 );
4088 }
4089
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 }
4129
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);
4144
4145 buffer.update(cx, |buffer, cx| {
4146 buffer.edit([(7..7, "NEW")], None, cx);
4147 });
4148
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 }
4169
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);
4183
4184 buffer.update(cx, |buffer, cx| {
4185 buffer.edit([(4..4, "NEW\n")], None, cx);
4186 });
4187
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 }
4208
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);
4222
4223 buffer.update(cx, |buffer, cx| {
4224 buffer.edit([(4..4, "NEW\n")], None, cx);
4225 });
4226
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);
4234 }
4235
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);
4250
4251 buffer.update(cx, |buffer, cx| {
4252 buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
4253 });
4254
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 }
4277
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);
4292
4293 buffer.update(cx, |buffer, cx| {
4294 buffer.edit([(4..4, "XXX\nYYY\n")], None, cx);
4295 });
4296
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);
4305 }
4306
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);
4321
4322 buffer.update(cx, |buffer, cx| {
4323 buffer.edit([(2..10, "YY\nZZ")], None, cx);
4324 });
4325
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 });
4364
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 );
4383 }
4384 }
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 }
2679}4425}