@@ -50,7 +50,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
let editor1 = cx.add_window({
let events = events.clone();
|cx| {
- cx.subscribe(cx.view(), move |_, _, event, _| {
+ let view = cx.view().clone();
+ cx.subscribe(&view, move |_, _, event, _| {
if matches!(event, Event::Edited | Event::BufferEdited) {
events.borrow_mut().push(("editor1", event.clone()));
}
@@ -63,7 +64,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
let editor2 = cx.add_window({
let events = events.clone();
|cx| {
- cx.subscribe(cx.view(), move |_, _, event, _| {
+ cx.subscribe(&cx.view().clone(), move |_, _, event, _| {
if matches!(event, Event::Edited | Event::BufferEdited) {
events.borrow_mut().push(("editor2", event.clone()));
}
@@ -156,7 +157,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
let mut now = Instant::now();
let buffer = cx.build_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
- let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
+ let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
@@ -496,10 +497,10 @@ fn test_clone(cx: &mut TestAppContext) {
);
assert_set_eq!(
cloned_editor
- .read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
+ .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
.unwrap(),
editor
- .read_with(cx, |editor, cx| editor.selections.ranges(cx))
+ .update(cx, |editor, cx| editor.selections.ranges(cx))
.unwrap()
);
assert_set_eq!(
@@ -523,7 +524,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
let project = Project::test(fs, [], cx).await;
let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
let pane = workspace
- .read_with(cx, |workspace, _| workspace.active_pane().clone())
+ .update(cx, |workspace, _| workspace.active_pane().clone())
.unwrap();
workspace.update(cx, |v, cx| {
@@ -1279,357 +1280,358 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
});
}
-#[gpui::test]
-async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
- init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
-
- let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
- let window = cx.window;
- window.simulate_resize(gpui::Point::new(100., 4. * line_height), &mut cx);
-
- cx.set_state(
- &r#"ˇone
- two
-
- three
- fourˇ
- five
-
- six"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
- cx.assert_editor_state(
- &r#"one
- two
- ˇ
- three
- four
- five
- ˇ
- six"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
- cx.assert_editor_state(
- &r#"one
- two
-
- three
- four
- five
- ˇ
- sixˇ"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
- cx.assert_editor_state(
- &r#"one
- two
-
- three
- four
- five
-
- sixˇ"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
- cx.assert_editor_state(
- &r#"one
- two
-
- three
- four
- five
- ˇ
- six"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
- cx.assert_editor_state(
- &r#"one
- two
- ˇ
- three
- four
- five
-
- six"#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
- cx.assert_editor_state(
- &r#"ˇone
- two
-
- three
- four
- five
-
- six"#
- .unindent(),
- );
-}
-
-#[gpui::test]
-async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
- init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
- let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
- let window = cx.window;
- window.simulate_resize(Point::new(1000., 4. * line_height + 0.5), &mut cx);
-
- cx.set_state(
- &r#"ˇone
- two
- three
- four
- five
- six
- seven
- eight
- nine
- ten
- "#,
- );
-
- cx.update_editor(|editor, cx| {
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 0.)
- );
- editor.scroll_screen(&ScrollAmount::Page(1.), cx);
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 3.)
- );
- editor.scroll_screen(&ScrollAmount::Page(1.), cx);
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 6.)
- );
- editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 3.)
- );
-
- editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 1.)
- );
- editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 3.)
- );
- });
-}
-
-#[gpui::test]
-async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
- init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
-
- let line_height = cx.update_editor(|editor, cx| {
- editor.set_vertical_scroll_margin(2, cx);
- editor.style(cx).text.line_height(cx.font_cache())
- });
-
- let window = cx.window;
- window.simulate_resize(gpui::Point::new(1000., 6.0 * line_height), &mut cx);
-
- cx.set_state(
- &r#"ˇone
- two
- three
- four
- five
- six
- seven
- eight
- nine
- ten
- "#,
- );
- cx.update_editor(|editor, cx| {
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 0.0)
- );
- });
-
- // Add a cursor below the visible area. Since both cursors cannot fit
- // on screen, the editor autoscrolls to reveal the newest cursor, and
- // allows the vertical scroll margin below that cursor.
- cx.update_editor(|editor, cx| {
- editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
- selections.select_ranges([
- Point::new(0, 0)..Point::new(0, 0),
- Point::new(6, 0)..Point::new(6, 0),
- ]);
- })
- });
- cx.update_editor(|editor, cx| {
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 3.0)
- );
- });
-
- // Move down. The editor cursor scrolls down to track the newest cursor.
- cx.update_editor(|editor, cx| {
- editor.move_down(&Default::default(), cx);
- });
- cx.update_editor(|editor, cx| {
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 4.0)
- );
- });
-
- // Add a cursor above the visible area. Since both cursors fit on screen,
- // the editor scrolls to show both.
- cx.update_editor(|editor, cx| {
- editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
- selections.select_ranges([
- Point::new(1, 0)..Point::new(1, 0),
- Point::new(6, 0)..Point::new(6, 0),
- ]);
- })
- });
- cx.update_editor(|editor, cx| {
- assert_eq!(
- editor.snapshot(cx).scroll_position(),
- gpui::Point::new(0., 1.0)
- );
- });
-}
-
-#[gpui::test]
-async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
- init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
-
- let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
- let window = cx.window;
- window.simulate_resize(gpui::Point::new(100., 4. * line_height), &mut cx);
-
- cx.set_state(
- &r#"
- ˇone
- two
- threeˇ
- four
- five
- six
- seven
- eight
- nine
- ten
- "#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
- cx.assert_editor_state(
- &r#"
- one
- two
- three
- ˇfour
- five
- sixˇ
- seven
- eight
- nine
- ten
- "#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
- cx.assert_editor_state(
- &r#"
- one
- two
- three
- four
- five
- six
- ˇseven
- eight
- nineˇ
- ten
- "#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
- cx.assert_editor_state(
- &r#"
- one
- two
- three
- ˇfour
- five
- sixˇ
- seven
- eight
- nine
- ten
- "#
- .unindent(),
- );
-
- cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
- cx.assert_editor_state(
- &r#"
- ˇone
- two
- threeˇ
- four
- five
- six
- seven
- eight
- nine
- ten
- "#
- .unindent(),
- );
-
- // Test select collapsing
- cx.update_editor(|editor, cx| {
- editor.move_page_down(&MovePageDown::default(), cx);
- editor.move_page_down(&MovePageDown::default(), cx);
- editor.move_page_down(&MovePageDown::default(), cx);
- });
- cx.assert_editor_state(
- &r#"
- one
- two
- three
- four
- five
- six
- seven
- eight
- nine
- ˇten
- ˇ"#
- .unindent(),
- );
-}
+//todo!(simulate_resize)
+// #[gpui::test]
+// async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+// let mut cx = EditorTestContext::new(cx).await;
+
+// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+// let window = cx.window;
+// window.simulate_resize(gpui::Point::new(100., 4. * line_height), &mut cx);
+
+// cx.set_state(
+// &r#"ˇone
+// two
+
+// three
+// fourˇ
+// five
+
+// six"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"one
+// two
+// ˇ
+// three
+// four
+// five
+// ˇ
+// six"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"one
+// two
+
+// three
+// four
+// five
+// ˇ
+// sixˇ"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"one
+// two
+
+// three
+// four
+// five
+
+// sixˇ"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"one
+// two
+
+// three
+// four
+// five
+// ˇ
+// six"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"one
+// two
+// ˇ
+// three
+// four
+// five
+
+// six"#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+// cx.assert_editor_state(
+// &r#"ˇone
+// two
+
+// three
+// four
+// five
+
+// six"#
+// .unindent(),
+// );
+// }
+
+// #[gpui::test]
+// async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+// let mut cx = EditorTestContext::new(cx).await;
+// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+// let window = cx.window;
+// window.simulate_resize(Point::new(1000., 4. * line_height + 0.5), &mut cx);
+
+// cx.set_state(
+// &r#"ˇone
+// two
+// three
+// four
+// five
+// six
+// seven
+// eight
+// nine
+// ten
+// "#,
+// );
+
+// cx.update_editor(|editor, cx| {
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 0.)
+// );
+// editor.scroll_screen(&ScrollAmount::Page(1.), cx);
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 3.)
+// );
+// editor.scroll_screen(&ScrollAmount::Page(1.), cx);
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 6.)
+// );
+// editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 3.)
+// );
+
+// editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 1.)
+// );
+// editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 3.)
+// );
+// });
+// }
+
+// #[gpui::test]
+// async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+// let mut cx = EditorTestContext::new(cx).await;
+
+// let line_height = cx.update_editor(|editor, cx| {
+// editor.set_vertical_scroll_margin(2, cx);
+// editor.style(cx).text.line_height(cx.font_cache())
+// });
+
+// let window = cx.window;
+// window.simulate_resize(gpui::Point::new(1000., 6.0 * line_height), &mut cx);
+
+// cx.set_state(
+// &r#"ˇone
+// two
+// three
+// four
+// five
+// six
+// seven
+// eight
+// nine
+// ten
+// "#,
+// );
+// cx.update_editor(|editor, cx| {
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 0.0)
+// );
+// });
+
+// // Add a cursor below the visible area. Since both cursors cannot fit
+// // on screen, the editor autoscrolls to reveal the newest cursor, and
+// // allows the vertical scroll margin below that cursor.
+// cx.update_editor(|editor, cx| {
+// editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
+// selections.select_ranges([
+// Point::new(0, 0)..Point::new(0, 0),
+// Point::new(6, 0)..Point::new(6, 0),
+// ]);
+// })
+// });
+// cx.update_editor(|editor, cx| {
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 3.0)
+// );
+// });
+
+// // Move down. The editor cursor scrolls down to track the newest cursor.
+// cx.update_editor(|editor, cx| {
+// editor.move_down(&Default::default(), cx);
+// });
+// cx.update_editor(|editor, cx| {
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 4.0)
+// );
+// });
+
+// // Add a cursor above the visible area. Since both cursors fit on screen,
+// // the editor scrolls to show both.
+// cx.update_editor(|editor, cx| {
+// editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
+// selections.select_ranges([
+// Point::new(1, 0)..Point::new(1, 0),
+// Point::new(6, 0)..Point::new(6, 0),
+// ]);
+// })
+// });
+// cx.update_editor(|editor, cx| {
+// assert_eq!(
+// editor.snapshot(cx).scroll_position(),
+// gpui::Point::new(0., 1.0)
+// );
+// });
+// }
+
+// #[gpui::test]
+// async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+// let mut cx = EditorTestContext::new(cx).await;
+
+// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+// let window = cx.window;
+// window.simulate_resize(gpui::Point::new(100., 4. * line_height), &mut cx);
+
+// cx.set_state(
+// &r#"
+// ˇone
+// two
+// threeˇ
+// four
+// five
+// six
+// seven
+// eight
+// nine
+// ten
+// "#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
+// cx.assert_editor_state(
+// &r#"
+// one
+// two
+// three
+// ˇfour
+// five
+// sixˇ
+// seven
+// eight
+// nine
+// ten
+// "#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
+// cx.assert_editor_state(
+// &r#"
+// one
+// two
+// three
+// four
+// five
+// six
+// ˇseven
+// eight
+// nineˇ
+// ten
+// "#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
+// cx.assert_editor_state(
+// &r#"
+// one
+// two
+// three
+// ˇfour
+// five
+// sixˇ
+// seven
+// eight
+// nine
+// ten
+// "#
+// .unindent(),
+// );
+
+// cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
+// cx.assert_editor_state(
+// &r#"
+// ˇone
+// two
+// threeˇ
+// four
+// five
+// six
+// seven
+// eight
+// nine
+// ten
+// "#
+// .unindent(),
+// );
+
+// // Test select collapsing
+// cx.update_editor(|editor, cx| {
+// editor.move_page_down(&MovePageDown::default(), cx);
+// editor.move_page_down(&MovePageDown::default(), cx);
+// editor.move_page_down(&MovePageDown::default(), cx);
+// });
+// cx.assert_editor_state(
+// &r#"
+// one
+// two
+// three
+// four
+// five
+// six
+// seven
+// eight
+// nine
+// ˇten
+// ˇ"#
+// .unindent(),
+// );
+// }
#[gpui::test]
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
@@ -3042,7 +3044,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
position: snapshot.anchor_after(Point::new(2, 0)),
disposition: BlockDisposition::Below,
height: 1,
- render: Arc::new(|_| Empty::new().into_any()),
+ render: Arc::new(|_| div().render()),
}],
Some(Autoscroll::fit()),
cx,
@@ -3148,245 +3150,246 @@ fn test_transpose(cx: &mut TestAppContext) {
});
}
+//todo!(clipboard)
+// #[gpui::test]
+// async fn test_clipboard(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+
+// let mut cx = EditorTestContext::new(cx).await;
+
+// cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
+// cx.update_editor(|e, cx| e.cut(&Cut, cx));
+// cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
+
+// // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
+// cx.set_state("two ˇfour ˇsix ˇ");
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
+
+// // Paste again but with only two cursors. Since the number of cursors doesn't
+// // match the number of slices in the clipboard, the entire clipboard text
+// // is pasted at each cursor.
+// cx.set_state("ˇtwo one✅ four three six five ˇ");
+// cx.update_editor(|e, cx| {
+// e.handle_input("( ", cx);
+// e.paste(&Paste, cx);
+// e.handle_input(") ", cx);
+// });
+// cx.assert_editor_state(
+// &([
+// "( one✅ ",
+// "three ",
+// "five ) ˇtwo one✅ four three six five ( one✅ ",
+// "three ",
+// "five ) ˇ",
+// ]
+// .join("\n")),
+// );
+
+// // Cut with three selections, one of which is full-line.
+// cx.set_state(indoc! {"
+// 1«2ˇ»3
+// 4ˇ567
+// «8ˇ»9"});
+// cx.update_editor(|e, cx| e.cut(&Cut, cx));
+// cx.assert_editor_state(indoc! {"
+// 1ˇ3
+// ˇ9"});
+
+// // Paste with three selections, noticing how the copied selection that was full-line
+// // gets inserted before the second cursor.
+// cx.set_state(indoc! {"
+// 1ˇ3
+// 9ˇ
+// «oˇ»ne"});
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// 12ˇ3
+// 4567
+// 9ˇ
+// 8ˇne"});
+
+// // Copy with a single cursor only, which writes the whole line into the clipboard.
+// cx.set_state(indoc! {"
+// The quick brown
+// fox juˇmps over
+// the lazy dog"});
+// cx.update_editor(|e, cx| e.copy(&Copy, cx));
+// cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
+
+// // Paste with three selections, noticing how the copied full-line selection is inserted
+// // before the empty selections but replaces the selection that is non-empty.
+// cx.set_state(indoc! {"
+// Tˇhe quick brown
+// «foˇ»x jumps over
+// tˇhe lazy dog"});
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// fox jumps over
+// Tˇhe quick brown
+// fox jumps over
+// ˇx jumps over
+// fox jumps over
+// tˇhe lazy dog"});
+// }
+
+// #[gpui::test]
+// async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
+// init_test(cx, |_| {});
+
+// let mut cx = EditorTestContext::new(cx).await;
+// let language = Arc::new(Language::new(
+// LanguageConfig::default(),
+// Some(tree_sitter_rust::language()),
+// ));
+// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+// // Cut an indented block, without the leading whitespace.
+// cx.set_state(indoc! {"
+// const a: B = (
+// c(),
+// «d(
+// e,
+// f
+// )ˇ»
+// );
+// "});
+// cx.update_editor(|e, cx| e.cut(&Cut, cx));
+// cx.assert_editor_state(indoc! {"
+// const a: B = (
+// c(),
+// ˇ
+// );
+// "});
+
+// // Paste it at the same position.
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// const a: B = (
+// c(),
+// d(
+// e,
+// f
+// )ˇ
+// );
+// "});
+
+// // Paste it at a line with a lower indent level.
+// cx.set_state(indoc! {"
+// ˇ
+// const a: B = (
+// c(),
+// );
+// "});
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// d(
+// e,
+// f
+// )ˇ
+// const a: B = (
+// c(),
+// );
+// "});
+
+// // Cut an indented block, with the leading whitespace.
+// cx.set_state(indoc! {"
+// const a: B = (
+// c(),
+// « d(
+// e,
+// f
+// )
+// ˇ»);
+// "});
+// cx.update_editor(|e, cx| e.cut(&Cut, cx));
+// cx.assert_editor_state(indoc! {"
+// const a: B = (
+// c(),
+// ˇ);
+// "});
+
+// // Paste it at the same position.
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// const a: B = (
+// c(),
+// d(
+// e,
+// f
+// )
+// ˇ);
+// "});
+
+// // Paste it at a line with a higher indent level.
+// cx.set_state(indoc! {"
+// const a: B = (
+// c(),
+// d(
+// e,
+// fˇ
+// )
+// );
+// "});
+// cx.update_editor(|e, cx| e.paste(&Paste, cx));
+// cx.assert_editor_state(indoc! {"
+// const a: B = (
+// c(),
+// d(
+// e,
+// f d(
+// e,
+// f
+// )
+// ˇ
+// )
+// );
+// "});
+// }
+
#[gpui::test]
-async fn test_clipboard(cx: &mut gpui::TestAppContext) {
+fn test_select_all(cx: &mut TestAppContext) {
init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
-
- cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
- cx.update_editor(|e, cx| e.cut(&Cut, cx));
- cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
-
- // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
- cx.set_state("two ˇfour ˇsix ˇ");
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
-
- // Paste again but with only two cursors. Since the number of cursors doesn't
- // match the number of slices in the clipboard, the entire clipboard text
- // is pasted at each cursor.
- cx.set_state("ˇtwo one✅ four three six five ˇ");
- cx.update_editor(|e, cx| {
- e.handle_input("( ", cx);
- e.paste(&Paste, cx);
- e.handle_input(") ", cx);
+ let view = cx.add_window(|cx| {
+ let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
+ build_editor(buffer, cx)
+ });
+ view.update(cx, |view, cx| {
+ view.select_all(&SelectAll, cx);
+ assert_eq!(
+ view.selections.display_ranges(cx),
+ &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
+ );
});
- cx.assert_editor_state(
- &([
- "( one✅ ",
- "three ",
- "five ) ˇtwo one✅ four three six five ( one✅ ",
- "three ",
- "five ) ˇ",
- ]
- .join("\n")),
- );
-
- // Cut with three selections, one of which is full-line.
- cx.set_state(indoc! {"
- 1«2ˇ»3
- 4ˇ567
- «8ˇ»9"});
- cx.update_editor(|e, cx| e.cut(&Cut, cx));
- cx.assert_editor_state(indoc! {"
- 1ˇ3
- ˇ9"});
-
- // Paste with three selections, noticing how the copied selection that was full-line
- // gets inserted before the second cursor.
- cx.set_state(indoc! {"
- 1ˇ3
- 9ˇ
- «oˇ»ne"});
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- 12ˇ3
- 4567
- 9ˇ
- 8ˇne"});
-
- // Copy with a single cursor only, which writes the whole line into the clipboard.
- cx.set_state(indoc! {"
- The quick brown
- fox juˇmps over
- the lazy dog"});
- cx.update_editor(|e, cx| e.copy(&Copy, cx));
- cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
-
- // Paste with three selections, noticing how the copied full-line selection is inserted
- // before the empty selections but replaces the selection that is non-empty.
- cx.set_state(indoc! {"
- Tˇhe quick brown
- «foˇ»x jumps over
- tˇhe lazy dog"});
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- fox jumps over
- Tˇhe quick brown
- fox jumps over
- ˇx jumps over
- fox jumps over
- tˇhe lazy dog"});
}
#[gpui::test]
-async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
+fn test_select_line(cx: &mut TestAppContext) {
init_test(cx, |_| {});
- let mut cx = EditorTestContext::new(cx).await;
- let language = Arc::new(Language::new(
- LanguageConfig::default(),
- Some(tree_sitter_rust::language()),
- ));
- cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-
- // Cut an indented block, without the leading whitespace.
- cx.set_state(indoc! {"
- const a: B = (
- c(),
- «d(
- e,
- f
- )ˇ»
- );
- "});
- cx.update_editor(|e, cx| e.cut(&Cut, cx));
- cx.assert_editor_state(indoc! {"
- const a: B = (
- c(),
- ˇ
+ let view = cx.add_window(|cx| {
+ let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
+ build_editor(buffer, cx)
+ });
+ view.update(cx, |view, cx| {
+ view.change_selections(None, cx, |s| {
+ s.select_display_ranges([
+ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+ DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+ DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
+ ])
+ });
+ view.select_line(&SelectLine, cx);
+ assert_eq!(
+ view.selections.display_ranges(cx),
+ vec![
+ DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
+ DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
+ ]
);
- "});
-
- // Paste it at the same position.
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- const a: B = (
- c(),
- d(
- e,
- f
- )ˇ
- );
- "});
-
- // Paste it at a line with a lower indent level.
- cx.set_state(indoc! {"
- ˇ
- const a: B = (
- c(),
- );
- "});
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- d(
- e,
- f
- )ˇ
- const a: B = (
- c(),
- );
- "});
-
- // Cut an indented block, with the leading whitespace.
- cx.set_state(indoc! {"
- const a: B = (
- c(),
- « d(
- e,
- f
- )
- ˇ»);
- "});
- cx.update_editor(|e, cx| e.cut(&Cut, cx));
- cx.assert_editor_state(indoc! {"
- const a: B = (
- c(),
- ˇ);
- "});
-
- // Paste it at the same position.
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- const a: B = (
- c(),
- d(
- e,
- f
- )
- ˇ);
- "});
-
- // Paste it at a line with a higher indent level.
- cx.set_state(indoc! {"
- const a: B = (
- c(),
- d(
- e,
- fˇ
- )
- );
- "});
- cx.update_editor(|e, cx| e.paste(&Paste, cx));
- cx.assert_editor_state(indoc! {"
- const a: B = (
- c(),
- d(
- e,
- f d(
- e,
- f
- )
- ˇ
- )
- );
- "});
-}
-
-#[gpui::test]
-fn test_select_all(cx: &mut TestAppContext) {
- init_test(cx, |_| {});
-
- let view = cx.add_window(|cx| {
- let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
- build_editor(buffer, cx)
- });
- view.update(cx, |view, cx| {
- view.select_all(&SelectAll, cx);
- assert_eq!(
- view.selections.display_ranges(cx),
- &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
- );
- });
-}
-
-#[gpui::test]
-fn test_select_line(cx: &mut TestAppContext) {
- init_test(cx, |_| {});
-
- let view = cx.add_window(|cx| {
- let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
- build_editor(buffer, cx)
- });
- view.update(cx, |view, cx| {
- view.change_selections(None, cx, |s| {
- s.select_display_ranges([
- DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
- DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
- DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
- DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
- ])
- });
- view.select_line(&SelectLine, cx);
- assert_eq!(
- view.selections.display_ranges(cx),
- vec![
- DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
- DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
- ]
- );
- });
+ });
view.update(cx, |view, cx| {
view.select_line(&SelectLine, cx);
@@ -1,28 +1,37 @@
use crate::{
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
};
+use collections::BTreeMap;
use futures::Future;
use gpui::{
AnyWindowHandle, AppContext, ForegroundExecutor, Keystroke, ModelContext, View, ViewContext,
+ VisualTestContext, WindowHandle,
};
use indoc::indoc;
+use itertools::Itertools;
use language::{Buffer, BufferSnapshot};
+use parking_lot::RwLock;
use project::{FakeFs, Project};
use std::{
any::TypeId,
ops::{Deref, DerefMut, Range},
+ sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+ },
};
use util::{
assert_set_eq,
test::{generate_marked_text, marked_text_ranges},
};
-// use super::build_editor_with_project;
+use super::build_editor_with_project;
pub struct EditorTestContext<'a> {
- pub cx: &'a mut gpui::TestAppContext,
+ pub cx: gpui::VisualTestContext<'a>,
pub window: AnyWindowHandle,
pub editor: View<Editor>,
+ pub assertion_cx: AssertionContextManager,
}
impl<'a> EditorTestContext<'a> {
@@ -43,15 +52,18 @@ impl<'a> EditorTestContext<'a> {
})
.await
.unwrap();
- let window = cx.add_window(|cx| {
- cx.focus_self();
- build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx)
+ let editor = cx.add_window(|cx| {
+ let editor =
+ build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx);
+ editor.focus(cx);
+ editor
});
- let editor = window.root(cx);
+ let editor_view = editor.root_view(cx).unwrap();
Self {
- cx,
- window: window.into(),
- editor,
+ cx: VisualTestContext::from_window(*editor.deref(), cx),
+ window: editor.into(),
+ editor: editor_view,
+ assertion_cx: AssertionContextManager::new(),
}
}
@@ -59,24 +71,27 @@ impl<'a> EditorTestContext<'a> {
&self,
predicate: impl FnMut(&Editor, &AppContext) -> bool,
) -> impl Future<Output = ()> {
- self.editor.condition(self.cx, predicate)
+ self.editor.condition::<crate::Event>(&self.cx, predicate)
}
- pub fn editor<F, T>(&self, read: F) -> T
+ #[track_caller]
+ pub fn editor<F, T>(&mut self, read: F) -> T
where
F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
{
- self.editor.update(self.cx, read)
+ self.editor
+ .update(&mut self.cx, |this, cx| read(&this, &cx))
}
+ #[track_caller]
pub fn update_editor<F, T>(&mut self, update: F) -> T
where
F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
{
- self.editor.update(self.cx, update)
+ self.editor.update(&mut self.cx, update)
}
- pub fn multibuffer<F, T>(&self, read: F) -> T
+ pub fn multibuffer<F, T>(&mut self, read: F) -> T
where
F: FnOnce(&MultiBuffer, &AppContext) -> T,
{
@@ -90,11 +105,11 @@ impl<'a> EditorTestContext<'a> {
self.update_editor(|editor, cx| editor.buffer().update(cx, update))
}
- pub fn buffer_text(&self) -> String {
+ pub fn buffer_text(&mut self) -> String {
self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
}
- pub fn buffer<F, T>(&self, read: F) -> T
+ pub fn buffer<F, T>(&mut self, read: F) -> T
where
F: FnOnce(&Buffer, &AppContext) -> T,
{
@@ -114,10 +129,18 @@ impl<'a> EditorTestContext<'a> {
})
}
- pub fn buffer_snapshot(&self) -> BufferSnapshot {
+ pub fn buffer_snapshot(&mut self) -> BufferSnapshot {
self.buffer(|buffer, _| buffer.snapshot())
}
+ pub fn add_assertion_context(&self, context: String) -> ContextHandle {
+ self.assertion_cx.add_context(context)
+ }
+
+ pub fn assertion_context(&self) -> String {
+ self.assertion_cx.context()
+ }
+
pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
let keystroke_under_test_handle =
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
@@ -141,16 +164,12 @@ impl<'a> EditorTestContext<'a> {
// before returning.
// NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
// quickly races with async actions.
- if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
- executor.run_until_parked();
- } else {
- unreachable!();
- }
+ self.cx.background_executor.run_until_parked();
keystrokes_under_test_handle
}
- pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
+ pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text);
ranges
@@ -160,12 +179,12 @@ impl<'a> EditorTestContext<'a> {
let ranges = self.ranges(marked_text);
let snapshot = self
.editor
- .update(self.cx, |editor, cx| editor.snapshot(cx));
+ .update(&mut self.cx, |editor, cx| editor.snapshot(cx));
ranges[0].start.to_display_point(&snapshot)
}
// Returns anchors for the current buffer using `«` and `»`
- pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
+ pub fn text_anchor_range(&mut self, marked_text: &str) -> Range<language::Anchor> {
let ranges = self.ranges(marked_text);
let snapshot = self.buffer_snapshot();
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
@@ -190,7 +209,7 @@ impl<'a> EditorTestContext<'a> {
marked_text.escape_debug().to_string()
));
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
- self.editor.update(self.cx, |editor, cx| {
+ self.editor.update(&mut self.cx, |editor, cx| {
editor.set_text(unmarked_text, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(selection_ranges)
@@ -206,7 +225,7 @@ impl<'a> EditorTestContext<'a> {
marked_text.escape_debug().to_string()
));
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
- self.editor.update(self.cx, |editor, cx| {
+ self.editor.update(&mut self.cx, |editor, cx| {
assert_eq!(editor.text(cx), unmarked_text);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(selection_ranges)
@@ -273,9 +292,12 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, expected_marked_text)
}
- fn editor_selections(&self) -> Vec<Range<usize>> {
+ #[track_caller]
+ fn editor_selections(&mut self) -> Vec<Range<usize>> {
self.editor
- .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
+ .update(&mut self.cx, |editor, cx| {
+ editor.selections.all::<usize>(cx)
+ })
.into_iter()
.map(|s| {
if s.reversed {
@@ -320,7 +342,7 @@ impl<'a> Deref for EditorTestContext<'a> {
type Target = gpui::TestAppContext;
fn deref(&self) -> &Self::Target {
- self.cx
+ &self.cx
}
}
@@ -329,3 +351,50 @@ impl<'a> DerefMut for EditorTestContext<'a> {
&mut self.cx
}
}
+
+/// Tracks string context to be printed when assertions fail.
+/// Often this is done by storing a context string in the manager and returning the handle.
+#[derive(Clone)]
+pub struct AssertionContextManager {
+ id: Arc<AtomicUsize>,
+ contexts: Arc<RwLock<BTreeMap<usize, String>>>,
+}
+
+impl AssertionContextManager {
+ pub fn new() -> Self {
+ Self {
+ id: Arc::new(AtomicUsize::new(0)),
+ contexts: Arc::new(RwLock::new(BTreeMap::new())),
+ }
+ }
+
+ pub fn add_context(&self, context: String) -> ContextHandle {
+ let id = self.id.fetch_add(1, Ordering::Relaxed);
+ let mut contexts = self.contexts.write();
+ contexts.insert(id, context);
+ ContextHandle {
+ id,
+ manager: self.clone(),
+ }
+ }
+
+ pub fn context(&self) -> String {
+ let contexts = self.contexts.read();
+ format!("\n{}\n", contexts.values().join("\n"))
+ }
+}
+
+/// Used to track the lifetime of a piece of context so that it can be provided when an assertion fails.
+/// For example, in the EditorTestContext, `set_state` returns a context handle so that if an assertion fails,
+/// the state that was set initially for the failure can be printed in the error message
+pub struct ContextHandle {
+ id: usize,
+ manager: AssertionContextManager,
+}
+
+impl Drop for ContextHandle {
+ fn drop(&mut self) {
+ let mut contexts = self.manager.contexts.write();
+ contexts.remove(&self.id);
+ }
+}