@@ -1,5 +1,5 @@
use crate::{
- AnchorRangeExt, DisplayPoint, Editor, MultiBuffer, RowExt,
+ AnchorRangeExt, DisplayPoint, Editor, ExcerptId, MultiBuffer, MultiBufferSnapshot, RowExt,
display_map::{HighlightKey, ToDisplayPoint},
};
use buffer_diff::DiffHunkStatusKind;
@@ -24,6 +24,7 @@ use std::{
atomic::{AtomicUsize, Ordering},
},
};
+use text::Selection;
use util::{
assert_set_eq,
test::{generate_marked_text, marked_text_ranges},
@@ -388,6 +389,23 @@ impl EditorTestContext {
#[track_caller]
pub fn assert_excerpts_with_selections(&mut self, marked_text: &str) {
+ let actual_text = self.to_format_multibuffer_as_marked_text();
+ let fmt_additional_notes = || {
+ struct Format<'a, T: std::fmt::Display>(&'a str, &'a T);
+
+ impl<T: std::fmt::Display> std::fmt::Display for Format<'_, T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "\n\n----- EXPECTED: -----\n\n{}\n\n----- ACTUAL: -----\n\n{}\n\n",
+ self.0, self.1
+ )
+ }
+ }
+
+ Format(marked_text, &actual_text)
+ };
+
let expected_excerpts = marked_text
.strip_prefix("[EXCERPT]\n")
.unwrap()
@@ -408,9 +426,10 @@ impl EditorTestContext {
assert!(
excerpts.len() == expected_excerpts.len(),
- "should have {} excerpts, got {}",
+ "should have {} excerpts, got {}{}",
expected_excerpts.len(),
- excerpts.len()
+ excerpts.len(),
+ fmt_additional_notes(),
);
for (ix, (excerpt_id, snapshot, range)) in excerpts.into_iter().enumerate() {
@@ -424,18 +443,25 @@ impl EditorTestContext {
if !expected_selections.is_empty() {
assert!(
is_selected,
- "excerpt {ix} should be selected. got {:?}",
+ "excerpt {ix} should contain selections. got {:?}{}",
self.editor_state(),
+ fmt_additional_notes(),
);
} else {
assert!(
!is_selected,
- "excerpt {ix} should not be selected, got: {selections:?}",
+ "excerpt {ix} should not contain selections, got: {selections:?}{}",
+ fmt_additional_notes(),
);
}
continue;
}
- assert!(!is_folded, "excerpt {} should not be folded", ix);
+ assert!(
+ !is_folded,
+ "excerpt {} should not be folded{}",
+ ix,
+ fmt_additional_notes()
+ );
assert_eq!(
multibuffer_snapshot
.text_for_range(Anchor::range_in_buffer(
@@ -444,7 +470,9 @@ impl EditorTestContext {
range.context.clone()
))
.collect::<String>(),
- expected_text
+ expected_text,
+ "{}",
+ fmt_additional_notes(),
);
let selections = selections
@@ -460,13 +488,38 @@ impl EditorTestContext {
.collect::<Vec<_>>();
// todo: selections that cross excerpt boundaries..
assert_eq!(
- selections, expected_selections,
- "excerpt {} has incorrect selections",
+ selections,
+ expected_selections,
+ "excerpt {} has incorrect selections{}",
ix,
+ fmt_additional_notes()
);
}
}
+ fn to_format_multibuffer_as_marked_text(&mut self) -> FormatMultiBufferAsMarkedText {
+ let (multibuffer_snapshot, selections, excerpts) = self.update_editor(|editor, _, cx| {
+ let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
+
+ let selections = editor.selections.disjoint_anchors_arc().to_vec();
+ let excerpts = multibuffer_snapshot
+ .excerpts()
+ .map(|(e_id, snapshot, range)| {
+ let is_folded = editor.is_buffer_folded(snapshot.remote_id(), cx);
+ (e_id, snapshot.clone(), range, is_folded)
+ })
+ .collect::<Vec<_>>();
+
+ (multibuffer_snapshot, selections, excerpts)
+ });
+
+ FormatMultiBufferAsMarkedText {
+ multibuffer_snapshot,
+ selections,
+ excerpts,
+ }
+ }
+
/// Make an assertion about the editor's text and the ranges and directions
/// of its selections using a string containing embedded range markers.
///
@@ -571,6 +624,63 @@ impl EditorTestContext {
}
}
+struct FormatMultiBufferAsMarkedText {
+ multibuffer_snapshot: MultiBufferSnapshot,
+ selections: Vec<Selection<Anchor>>,
+ excerpts: Vec<(ExcerptId, BufferSnapshot, ExcerptRange<text::Anchor>, bool)>,
+}
+
+impl std::fmt::Display for FormatMultiBufferAsMarkedText {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let Self {
+ multibuffer_snapshot,
+ selections,
+ excerpts,
+ } = self;
+
+ for (excerpt_id, snapshot, range, is_folded) in excerpts.into_iter() {
+ write!(f, "[EXCERPT]\n")?;
+ if *is_folded {
+ write!(f, "[FOLDED]\n")?;
+ }
+
+ let mut text = multibuffer_snapshot
+ .text_for_range(Anchor::range_in_buffer(
+ *excerpt_id,
+ snapshot.remote_id(),
+ range.context.clone(),
+ ))
+ .collect::<String>();
+
+ let selections = selections
+ .iter()
+ .filter(|&s| s.head().excerpt_id == *excerpt_id)
+ .map(|s| {
+ let head = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot)
+ - text::ToOffset::to_offset(&range.context.start, &snapshot);
+ let tail = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot)
+ - text::ToOffset::to_offset(&range.context.start, &snapshot);
+ tail..head
+ })
+ .rev()
+ .collect::<Vec<_>>();
+
+ for selection in selections {
+ if selection.is_empty() {
+ text.insert(selection.start, 'ห');
+ continue;
+ }
+ text.insert(selection.end, 'ยป');
+ text.insert(selection.start, 'ยซ');
+ }
+
+ write!(f, "{text}")?;
+ }
+
+ Ok(())
+ }
+}
+
#[track_caller]
pub fn assert_state_with_diff(
editor: &Entity<Editor>,
@@ -1619,14 +1619,13 @@ mod tests {
project_diff::{self, ProjectDiff},
};
- #[cfg_attr(windows, ignore = "currently fails on windows")]
#[gpui::test]
async fn test_go_to_prev_hunk_multibuffer(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
".git": {},
"a.txt": "created\n",
@@ -1637,7 +1636,7 @@ mod tests {
.await;
fs.set_head_and_index_for_repo(
- Path::new("/a/.git"),
+ Path::new(path!("/a/.git")),
&[
("b.txt", "before\n".to_string()),
("c.txt", "unchanged\n".to_string()),
@@ -1645,7 +1644,7 @@ mod tests {
],
);
- let project = Project::test(fs, [Path::new("/a")], cx).await;
+ let project = Project::test(fs, [Path::new(path!("/a"))], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
@@ -1707,7 +1706,6 @@ mod tests {
));
}
- #[cfg_attr(windows, ignore = "currently fails on windows")]
#[gpui::test]
async fn test_excerpts_splitting_after_restoring_the_middle_excerpt(cx: &mut TestAppContext) {
init_test(cx);
@@ -1747,7 +1745,7 @@ mod tests {
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
- "/a",
+ path!("/a"),
json!({
".git": {},
"main.rs": buffer_contents,
@@ -1756,11 +1754,11 @@ mod tests {
.await;
fs.set_head_and_index_for_repo(
- Path::new("/a/.git"),
+ Path::new(path!("/a/.git")),
&[("main.rs", git_contents.to_owned())],
);
- let project = Project::test(fs, [Path::new("/a")], cx).await;
+ let project = Project::test(fs, [Path::new(path!("/a"))], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
@@ -1925,6 +1923,7 @@ mod tests {
cx.run_until_parked();
let editor = diff.read_with(cx, |diff, _| diff.editor.clone());
+
assert_state_with_diff(
&editor,
cx,