This commit is contained in:
Nathan Sobo 2023-08-03 17:03:39 -06:00
parent 3c938a7377
commit afcc0d621b
13 changed files with 300 additions and 262 deletions

View file

@ -64,7 +64,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, cx)
} }
}) })
.detach(cx); .root(cx);
let editor2 = cx let editor2 = cx
.add_window({ .add_window({
let events = events.clone(); let events = events.clone();
@ -81,7 +81,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, cx)
} }
}) })
.detach(cx); .root(cx);
assert_eq!(mem::take(&mut *events.borrow_mut()), []); assert_eq!(mem::take(&mut *events.borrow_mut()), []);
// Mutating editor 1 will emit an `Edited` event only for that editor. // Mutating editor 1 will emit an `Edited` event only for that editor.
@ -179,7 +179,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx let editor = cx
.add_window(|cx| build_editor(buffer.clone(), cx)) .add_window(|cx| build_editor(buffer.clone(), cx))
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.start_transaction_at(now, cx); editor.start_transaction_at(now, cx);
@ -354,7 +354,7 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
editor.update(cx, |view, cx| { editor.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
}); });
@ -423,7 +423,7 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
@ -471,7 +471,7 @@ fn test_clone(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&text, cx); let buffer = MultiBuffer::build_simple(&text, cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone())); editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
@ -489,7 +489,7 @@ fn test_clone(cx: &mut TestAppContext) {
.update(cx, |editor, cx| { .update(cx, |editor, cx| {
cx.add_window(Default::default(), |cx| editor.clone(cx)) cx.add_window(Default::default(), |cx| editor.clone(cx))
}) })
.detach(cx); .root(cx);
let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)); let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)); let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
@ -639,7 +639,7 @@ fn test_cancel(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx); view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
@ -704,7 +704,7 @@ fn test_fold_action(cx: &mut TestAppContext) {
); );
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -774,7 +774,7 @@ fn test_move_cursor(cx: &mut TestAppContext) {
let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx)); let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
let view = cx let view = cx
.add_window(|cx| build_editor(buffer.clone(), cx)) .add_window(|cx| build_editor(buffer.clone(), cx))
.detach(cx); .root(cx);
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
@ -854,7 +854,7 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
assert_eq!('ⓐ'.len_utf8(), 3); assert_eq!('ⓐ'.len_utf8(), 3);
assert_eq!('α'.len_utf8(), 2); assert_eq!('α'.len_utf8(), 2);
@ -961,7 +961,7 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]); s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
@ -1013,7 +1013,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\n def", cx); let buffer = MultiBuffer::build_simple("abc\n def", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -1178,7 +1178,7 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -1233,7 +1233,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.set_wrap_width(Some(140.), cx); view.set_wrap_width(Some(140.), cx);
@ -1568,7 +1568,7 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("one two three four", cx); let buffer = MultiBuffer::build_simple("one two three four", cx);
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -1606,7 +1606,7 @@ fn test_newline(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -1651,7 +1651,7 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
}); });
editor editor
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
// Edit the buffer directly, deleting ranges surrounding the editor's selections // Edit the buffer directly, deleting ranges surrounding the editor's selections
@ -1863,7 +1863,7 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20])); editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
editor editor
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
// Edit the buffer directly, deleting ranges surrounding the editor's selections // Edit the buffer directly, deleting ranges surrounding the editor's selections
@ -2375,7 +2375,7 @@ fn test_delete_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -2400,7 +2400,7 @@ fn test_delete_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)]) s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
@ -2704,7 +2704,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -2732,7 +2732,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -2761,7 +2761,7 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.fold_ranges( view.fold_ranges(
vec![ vec![
@ -2862,7 +2862,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
let snapshot = editor.buffer.read(cx).snapshot(cx); let snapshot = editor.buffer.read(cx).snapshot(cx);
editor.insert_blocks( editor.insert_blocks(
@ -3182,7 +3182,7 @@ fn test_select_all(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.select_all(&SelectAll, cx); view.select_all(&SelectAll, cx);
assert_eq!( assert_eq!(
@ -3201,7 +3201,7 @@ fn test_select_line(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
s.select_display_ranges([ s.select_display_ranges([
@ -3250,7 +3250,7 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.fold_ranges( view.fold_ranges(
vec![ vec![
@ -3323,7 +3323,7 @@ fn test_add_selection_above_below(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
build_editor(buffer, cx) build_editor(buffer, cx)
}) })
.detach(cx); .root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -3608,7 +3608,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
.await; .await;
@ -3771,7 +3771,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor editor
.condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx)) .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
.await; .await;
@ -4334,7 +4334,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
.await; .await;
@ -4482,7 +4482,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor editor
.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
.await; .await;
@ -4572,7 +4572,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) {
); );
let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap(); let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
@ -4702,7 +4702,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
assert!(cx.read(|cx| editor.is_dirty(cx))); assert!(cx.read(|cx| editor.is_dirty(cx)));
@ -4814,7 +4814,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
assert!(cx.read(|cx| editor.is_dirty(cx))); assert!(cx.read(|cx| editor.is_dirty(cx)));
@ -4928,7 +4928,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
let format = editor.update(cx, |editor, cx| { let format = editor.update(cx, |editor, cx| {
@ -5706,7 +5706,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
assert_eq!(view.text(cx), "aaaa\nbbbb"); assert_eq!(view.text(cx), "aaaa\nbbbb");
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -5776,7 +5776,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
let (expected_text, selection_ranges) = marked_text_ranges( let (expected_text, selection_ranges) = marked_text_ranges(
indoc! {" indoc! {"
@ -5869,7 +5869,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
); );
editor editor
}) })
.detach(cx); .root(cx);
// Refreshing selections is a no-op when excerpts haven't changed. // Refreshing selections is a no-op when excerpts haven't changed.
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -5950,7 +5950,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
); );
editor editor
}) })
.detach(cx); .root(cx);
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {
multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx); multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
@ -6013,7 +6013,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
.await; .await;
@ -6054,7 +6054,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), cx)
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
struct Type1; struct Type1;
@ -6145,7 +6145,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
}); });
let leader = cx let leader = cx
.add_window(|cx| build_editor(buffer.clone(), cx)) .add_window(|cx| build_editor(buffer.clone(), cx))
.detach(cx); .root(cx);
let follower = cx let follower = cx
.update(|cx| { .update(|cx| {
cx.add_window( cx.add_window(
@ -6156,7 +6156,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|cx| build_editor(buffer.clone(), cx), |cx| build_editor(buffer.clone(), cx),
) )
}) })
.detach(cx); .root(cx);
let is_still_following = Rc::new(RefCell::new(true)); let is_still_following = Rc::new(RefCell::new(true));
let follower_edit_event_count = Rc::new(RefCell::new(0)); let follower_edit_event_count = Rc::new(RefCell::new(0));
@ -6289,7 +6289,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
let leader = pane.update(cx, |_, cx| { let leader = pane.update(cx, |_, cx| {
@ -7033,7 +7033,7 @@ async fn test_copilot_multibuffer(
); );
multibuffer multibuffer
}); });
let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
handle_copilot_completion_request( handle_copilot_completion_request(
&copilot_lsp, &copilot_lsp,
@ -7163,7 +7163,7 @@ async fn test_copilot_disabled_globs(
); );
multibuffer multibuffer
}); });
let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
let mut copilot_requests = copilot_lsp let mut copilot_requests = copilot_lsp
.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move { .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
@ -7244,7 +7244,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()

View file

@ -3007,7 +3007,7 @@ mod tests {
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
Editor::new(EditorMode::Full, buffer, None, None, cx) Editor::new(EditorMode::Full, buffer, None, None, cx)
}) })
.detach(cx); .root(cx);
let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
let layouts = editor.update(cx, |editor, cx| { let layouts = editor.update(cx, |editor, cx| {
@ -3028,7 +3028,7 @@ mod tests {
let buffer = MultiBuffer::build_simple("", cx); let buffer = MultiBuffer::build_simple("", cx);
Editor::new(EditorMode::Full, buffer, None, None, cx) Editor::new(EditorMode::Full, buffer, None, None, cx)
}) })
.detach(cx); .root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.set_placeholder_text("hello", cx); editor.set_placeholder_text("hello", cx);
@ -3240,7 +3240,7 @@ mod tests {
let buffer = MultiBuffer::build_simple(&input_text, cx); let buffer = MultiBuffer::build_simple(&input_text, cx);
Editor::new(editor_mode, buffer, None, None, cx) Editor::new(editor_mode, buffer, None, None, cx)
}) })
.detach(cx); .root(cx);
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
let (_, layout_state) = editor.update(cx, |editor, cx| { let (_, layout_state) = editor.update(cx, |editor, cx| {

View file

@ -1138,7 +1138,7 @@ mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
@ -1840,7 +1840,7 @@ mod tests {
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
@ -1995,7 +1995,7 @@ mod tests {
}); });
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
@ -2083,7 +2083,7 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let editor = cx let editor = cx
.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
.detach(cx); .root(cx);
let editor_edited = Arc::new(AtomicBool::new(false)); let editor_edited = Arc::new(AtomicBool::new(false));
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let closure_editor_edited = Arc::clone(&editor_edited); let closure_editor_edited = Arc::clone(&editor_edited);
@ -2337,7 +2337,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
}); });
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
@ -2384,7 +2384,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let editor = cx let editor = cx
.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
.detach(cx); .root(cx);
let editor_edited = Arc::new(AtomicBool::new(false)); let editor_edited = Arc::new(AtomicBool::new(false));
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let closure_editor_edited = Arc::clone(&editor_edited); let closure_editor_edited = Arc::clone(&editor_edited);
@ -2574,7 +2574,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let worktree_id = workspace.update(cx, |workspace, cx| { let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| { workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()

View file

@ -842,7 +842,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let finder = cx let finder = cx
.add_window(|cx| { .add_window(|cx| {
Picker::new( Picker::new(
@ -856,7 +856,7 @@ mod tests {
cx, cx,
) )
}) })
.detach(cx); .root(cx);
let query = test_path_like("hi"); let query = test_path_like("hi");
finder finder
@ -940,7 +940,7 @@ mod tests {
.await; .await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let finder = cx let finder = cx
.add_window(|cx| { .add_window(|cx| {
Picker::new( Picker::new(
@ -954,7 +954,7 @@ mod tests {
cx, cx,
) )
}) })
.detach(cx); .root(cx);
finder finder
.update(cx, |f, cx| { .update(cx, |f, cx| {
f.delegate_mut().spawn_search(test_path_like("hi"), cx) f.delegate_mut().spawn_search(test_path_like("hi"), cx)
@ -980,7 +980,7 @@ mod tests {
.await; .await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let finder = cx let finder = cx
.add_window(|cx| { .add_window(|cx| {
Picker::new( Picker::new(
@ -994,7 +994,7 @@ mod tests {
cx, cx,
) )
}) })
.detach(cx); .root(cx);
// Even though there is only one worktree, that worktree's filename // Even though there is only one worktree, that worktree's filename
// is included in the matching, because the worktree is a single file. // is included in the matching, because the worktree is a single file.
@ -1051,7 +1051,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let worktree_id = cx.read(|cx| { let worktree_id = cx.read(|cx| {
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>(); let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1); assert_eq!(worktrees.len(), 1);
@ -1078,7 +1078,7 @@ mod tests {
cx, cx,
) )
}) })
.detach(cx); .root(cx);
finder finder
.update(cx, |f, cx| { .update(cx, |f, cx| {
@ -1117,7 +1117,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let finder = cx let finder = cx
.add_window(|cx| { .add_window(|cx| {
Picker::new( Picker::new(
@ -1131,7 +1131,7 @@ mod tests {
cx, cx,
) )
}) })
.detach(cx); .root(cx);
finder finder
.update(cx, |f, cx| { .update(cx, |f, cx| {
f.delegate_mut().spawn_search(test_path_like("dir"), cx) f.delegate_mut().spawn_search(test_path_like("dir"), cx)

View file

@ -130,12 +130,12 @@ pub trait BorrowAppContext {
} }
pub trait BorrowWindowContext { pub trait BorrowWindowContext {
type Return<T>; type Result<T>;
fn read_with<T, F>(&self, window_id: usize, f: F) -> Self::Return<T> fn read_window_with<T, F>(&self, window_id: usize, f: F) -> Self::Result<T>
where where
F: FnOnce(&WindowContext) -> T; F: FnOnce(&WindowContext) -> T;
fn update<T, F>(&mut self, window_id: usize, f: F) -> Self::Return<T> fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T>
where where
F: FnOnce(&mut WindowContext) -> T; F: FnOnce(&mut WindowContext) -> T;
} }
@ -458,6 +458,26 @@ impl BorrowAppContext for AsyncAppContext {
} }
} }
impl BorrowWindowContext for AsyncAppContext {
type Result<T> = Option<T>;
fn read_window_with<T, F>(&self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&WindowContext) -> T,
{
self.0.borrow().read_with(|cx| cx.read_window(window_id, f))
}
fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&mut WindowContext) -> T,
{
self.0
.borrow_mut()
.update(|cx| cx.update_window(window_id, f))
}
}
type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize); type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext); type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
@ -2162,6 +2182,24 @@ impl BorrowAppContext for AppContext {
} }
} }
impl BorrowWindowContext for AppContext {
type Result<T> = Option<T>;
fn read_window_with<T, F>(&self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&WindowContext) -> T,
{
AppContext::read_window(self, window_id, f)
}
fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&mut WindowContext) -> T,
{
AppContext::update_window(self, window_id, f)
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum ParentId { pub enum ParentId {
View(usize), View(usize),
@ -3360,14 +3398,18 @@ impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
} }
impl<V> BorrowWindowContext for ViewContext<'_, '_, V> { impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
type Return<T> = T; type Result<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(&*self.window_context, window_id, f) BorrowWindowContext::read_window_with(&*self.window_context, window_id, f)
} }
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T { fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
BorrowWindowContext::update(&mut *self.window_context, window_id, f) &mut self,
window_id: usize,
f: F,
) -> T {
BorrowWindowContext::update_window(&mut *self.window_context, window_id, f)
} }
} }
@ -3467,14 +3509,18 @@ impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
} }
impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> { impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
type Return<T> = T; type Result<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(&*self.view_context, window_id, f) BorrowWindowContext::read_window_with(&*self.view_context, window_id, f)
} }
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T { fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
BorrowWindowContext::update(&mut *self.view_context, window_id, f) &mut self,
window_id: usize,
f: F,
) -> T {
BorrowWindowContext::update_window(&mut *self.view_context, window_id, f)
} }
} }
@ -3521,14 +3567,18 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
} }
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> { impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
type Return<T> = T; type Result<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(&*self.view_context, window_id, f) BorrowWindowContext::read_window_with(&*self.view_context, window_id, f)
} }
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T { fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
BorrowWindowContext::update(&mut *self.view_context, window_id, f) &mut self,
window_id: usize,
f: F,
) -> T {
BorrowWindowContext::update_window(&mut *self.view_context, window_id, f)
} }
} }
@ -3830,32 +3880,16 @@ impl<V: View> WindowHandle<V> {
self.any_handle.id() self.any_handle.id()
} }
pub fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle<V> { pub fn root<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<ViewHandle<V>> {
self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap())
} }
/// Keep this window open until it's explicitly closed. pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
//
// TODO: Implement window dropping behavior when we don't call this.
pub fn detach(mut self, cx: &impl BorrowAppContext) -> ViewHandle<V> {
let root = self.root(cx);
let ref_counts = self.any_handle.ref_counts.take();
#[cfg(any(test, feature = "test-support"))]
ref_counts
.unwrap()
.lock()
.leak_detector
.lock()
.handle_dropped(self.id(), self.any_handle.handle_id);
root
}
pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> R
where where
C: BorrowAppContext, C: BorrowWindowContext,
F: FnOnce(&WindowContext) -> R, F: FnOnce(&WindowContext) -> R,
{ {
cx.read_with(|cx| cx.read_window(self.id(), read).unwrap()) cx.read_window_with(self.id(), |cx| read(cx))
} }
pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> R pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> R
@ -3891,9 +3925,9 @@ impl<V: View> WindowHandle<V> {
root_view.read(cx) root_view.read(cx)
} }
pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> R pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where where
C: BorrowAppContext, C: BorrowWindowContext,
F: FnOnce(&V, &ViewContext<V>) -> R, F: FnOnce(&V, &ViewContext<V>) -> R,
{ {
self.read_with(cx, |cx| { self.read_with(cx, |cx| {
@ -4021,25 +4055,25 @@ impl<T: View> ViewHandle<T> {
cx.read_view(self) cx.read_view(self)
} }
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Return<S> pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Result<S>
where where
C: BorrowWindowContext, C: BorrowWindowContext,
F: FnOnce(&T, &ViewContext<T>) -> S, F: FnOnce(&T, &ViewContext<T>) -> S,
{ {
cx.read_with(self.window_id, |cx| { cx.read_window_with(self.window_id, |cx| {
let cx = ViewContext::immutable(cx, self.view_id); let cx = ViewContext::immutable(cx, self.view_id);
read(cx.read_view(self), &cx) read(cx.read_view(self), &cx)
}) })
} }
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Return<S> pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Result<S>
where where
C: BorrowWindowContext, C: BorrowWindowContext,
F: FnOnce(&mut T, &mut ViewContext<T>) -> S, F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
{ {
let mut update = Some(update); let mut update = Some(update);
cx.update(self.window_id, |cx| { cx.update_window(self.window_id, |cx| {
cx.update_view(self, &mut |view, cx| { cx.update_view(self, &mut |view, cx| {
let update = update.take().unwrap(); let update = update.take().unwrap();
update(view, cx) update(view, cx)
@ -5005,7 +5039,7 @@ mod tests {
} }
#[crate::test(self)] #[crate::test(self)]
fn test_entity_release_hooks(cx: &mut AppContext) { fn test_entity_release_hooks(cx: &mut TestAppContext) {
struct Model { struct Model {
released: Rc<Cell<bool>>, released: Rc<Cell<bool>>,
} }
@ -5048,7 +5082,7 @@ mod tests {
let model = cx.add_model(|_| Model { let model = cx.add_model(|_| Model {
released: model_released.clone(), released: model_released.clone(),
}); });
let window = cx.add_window(Default::default(), |_| View { let window = cx.add_window(|_| View {
released: view_released.clone(), released: view_released.clone(),
}); });
let view = window.root(cx); let view = window.root(cx);
@ -5056,16 +5090,18 @@ mod tests {
assert!(!model_released.get()); assert!(!model_released.get());
assert!(!view_released.get()); assert!(!view_released.get());
cx.observe_release(&model, { cx.update(|cx| {
let model_release_observed = model_release_observed.clone(); cx.observe_release(&model, {
move |_, _| model_release_observed.set(true) let model_release_observed = model_release_observed.clone();
}) move |_, _| model_release_observed.set(true)
.detach(); })
cx.observe_release(&view, { .detach();
let view_release_observed = view_release_observed.clone(); cx.observe_release(&view, {
move |_, _| view_release_observed.set(true) let view_release_observed = view_release_observed.clone();
}) move |_, _| view_release_observed.set(true)
.detach(); })
.detach();
});
cx.update(move |_| { cx.update(move |_| {
drop(model); drop(model);
@ -5795,7 +5831,7 @@ mod tests {
} }
#[crate::test(self)] #[crate::test(self)]
fn test_dispatch_action(cx: &mut AppContext) { fn test_dispatch_action(cx: &mut TestAppContext) {
struct ViewA { struct ViewA {
id: usize, id: usize,
child: Option<AnyViewHandle>, child: Option<AnyViewHandle>,
@ -5846,68 +5882,70 @@ mod tests {
impl_actions!(test, [Action]); impl_actions!(test, [Action]);
let actions = Rc::new(RefCell::new(Vec::new())); let actions = Rc::new(RefCell::new(Vec::new()));
cx.add_global_action({
let actions = actions.clone();
move |_: &Action, _: &mut AppContext| {
actions.borrow_mut().push("global".to_string());
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewA, action: &Action, cx| {
assert_eq!(action.0, "bar");
cx.propagate_action();
actions.borrow_mut().push(format!("{} a", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewA, _: &Action, cx| {
if view.id != 1 {
cx.add_view(|cx| {
cx.propagate_action(); // Still works on a nested ViewContext
ViewB { id: 5, child: None }
});
}
actions.borrow_mut().push(format!("{} b", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} c", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} d", view.id));
}
});
cx.capture_action({
let actions = actions.clone();
move |view: &mut ViewA, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} capture", view.id));
}
});
let observed_actions = Rc::new(RefCell::new(Vec::new())); let observed_actions = Rc::new(RefCell::new(Vec::new()));
cx.observe_actions({
let observed_actions = observed_actions.clone();
move |action_id, _| observed_actions.borrow_mut().push(action_id)
})
.detach();
let window = cx.add_window(Default::default(), |_| ViewA { id: 1, child: None }); cx.update(|cx| {
cx.add_global_action({
let actions = actions.clone();
move |_: &Action, _: &mut AppContext| {
actions.borrow_mut().push("global".to_string());
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewA, action: &Action, cx| {
assert_eq!(action.0, "bar");
cx.propagate_action();
actions.borrow_mut().push(format!("{} a", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewA, _: &Action, cx| {
if view.id != 1 {
cx.add_view(|cx| {
cx.propagate_action(); // Still works on a nested ViewContext
ViewB { id: 5, child: None }
});
}
actions.borrow_mut().push(format!("{} b", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} c", view.id));
}
});
cx.add_action({
let actions = actions.clone();
move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} d", view.id));
}
});
cx.capture_action({
let actions = actions.clone();
move |view: &mut ViewA, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} capture", view.id));
}
});
cx.observe_actions({
let observed_actions = observed_actions.clone();
move |action_id, _| observed_actions.borrow_mut().push(action_id)
})
.detach();
});
let window = cx.add_window(|_| ViewA { id: 1, child: None });
let view_1 = window.root(cx); let view_1 = window.root(cx);
let view_2 = window.update(cx, |cx| { let view_2 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewB { id: 2, child: None }); let child = cx.add_view(|_| ViewB { id: 2, child: None });
@ -5956,7 +5994,7 @@ mod tests {
// Remove view_1, which doesn't propagate the action // Remove view_1, which doesn't propagate the action
let window = cx.add_window(Default::default(), |_| ViewB { id: 2, child: None }); let window = cx.add_window(|_| ViewB { id: 2, child: None });
let view_2 = window.root(cx); let view_2 = window.root(cx);
let view_3 = window.update(cx, |cx| { let view_3 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None }); let child = cx.add_view(|_| ViewA { id: 3, child: None });
@ -6457,7 +6495,7 @@ mod tests {
} }
#[crate::test(self)] #[crate::test(self)]
fn test_refresh_windows(cx: &mut AppContext) { fn test_refresh_windows(cx: &mut TestAppContext) {
struct View(usize); struct View(usize);
impl super::Entity for View { impl super::Entity for View {
@ -6474,7 +6512,7 @@ mod tests {
} }
} }
let window = cx.add_window(Default::default(), |_| View(0)); let window = cx.add_window(|_| View(0));
let root_view = window.root(cx); let root_view = window.root(cx);
window.update(cx, |cx| { window.update(cx, |cx| {
assert_eq!( assert_eq!(

View file

@ -406,16 +406,20 @@ impl BorrowAppContext for TestAppContext {
} }
impl BorrowWindowContext for TestAppContext { impl BorrowWindowContext for TestAppContext {
type Return<T> = T; type Result<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
self.cx self.cx
.borrow() .borrow()
.read_window(window_id, f) .read_window(window_id, f)
.expect("window was closed") .expect("window was closed")
} }
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T { fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
f: F,
) -> T {
self.cx self.cx
.borrow_mut() .borrow_mut()
.update_window(window_id, f) .update_window(window_id, f)

View file

@ -142,9 +142,9 @@ impl BorrowAppContext for WindowContext<'_> {
} }
impl BorrowWindowContext for WindowContext<'_> { impl BorrowWindowContext for WindowContext<'_> {
type Return<T> = T; type Result<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
if self.window_id == window_id { if self.window_id == window_id {
f(self) f(self)
} else { } else {
@ -152,7 +152,11 @@ impl BorrowWindowContext for WindowContext<'_> {
} }
} }
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T { fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
f: F,
) -> T {
if self.window_id == window_id { if self.window_id == window_id {
f(self) f(self)
} else { } else {

View file

@ -63,7 +63,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
let log_view = cx let log_view = cx
.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)) .add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx))
.detach(cx); .root(cx);
language_server.notify::<lsp::notification::LogMessage>(lsp::LogMessageParams { language_server.notify::<lsp::notification::LogMessage>(lsp::LogMessageParams {
message: "hello from the server".into(), message: "hello from the server".into(),

View file

@ -1782,7 +1782,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
assert_eq!( assert_eq!(
visible_entries_as_strings(&panel, 0..50, cx), visible_entries_as_strings(&panel, 0..50, cx),
@ -2327,7 +2327,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
panel.update(cx, |panel, cx| { panel.update(cx, |panel, cx| {
@ -2641,7 +2641,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
let new_search_events_count = Arc::new(AtomicUsize::new(0)); let new_search_events_count = Arc::new(AtomicUsize::new(0));
@ -2730,7 +2730,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
panel.update(cx, |panel, cx| { panel.update(cx, |panel, cx| {

View file

@ -1449,7 +1449,7 @@ pub mod tests {
let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
let search_view = cx let search_view = cx
.add_window(|cx| ProjectSearchView::new(search.clone(), cx)) .add_window(|cx| ProjectSearchView::new(search.clone(), cx))
.detach(cx); .root(cx);
search_view.update(cx, |search_view, cx| { search_view.update(cx, |search_view, cx| {
search_view search_view
@ -1754,7 +1754,7 @@ pub mod tests {
}); });
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let active_item = cx.read(|cx| { let active_item = cx.read(|cx| {
workspace workspace

View file

@ -1072,7 +1072,7 @@ mod tests {
let project = Project::test(params.fs.clone(), [], cx).await; let project = Project::test(params.fs.clone(), [], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
(project, workspace) (project, workspace)
} }

View file

@ -793,68 +793,60 @@ impl Workspace {
DB.next_id().await.unwrap_or(0) DB.next_id().await.unwrap_or(0)
}; };
let window = requesting_window_id let window = requesting_window_id.and_then(|window_id| {
.and_then(|window_id| { cx.update(|cx| {
cx.update(|cx| { cx.replace_root_view(window_id, |cx| {
cx.replace_root_view(window_id, |cx| { Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
Workspace::new(
workspace_id,
project_handle.clone(),
app_state.clone(),
cx,
)
})
}) })
}) })
.unwrap_or_else(|| { });
let window_bounds_override = window_bounds_env_override(&cx); let window = window.unwrap_or_else(|| {
let (bounds, display) = if let Some(bounds) = window_bounds_override { let window_bounds_override = window_bounds_env_override(&cx);
(Some(bounds), None) let (bounds, display) = if let Some(bounds) = window_bounds_override {
} else { (Some(bounds), None)
serialized_workspace } else {
.as_ref() serialized_workspace
.and_then(|serialized_workspace| { .as_ref()
let display = serialized_workspace.display?; .and_then(|serialized_workspace| {
let mut bounds = serialized_workspace.bounds?; let display = serialized_workspace.display?;
let mut bounds = serialized_workspace.bounds?;
// Stored bounds are relative to the containing display. // Stored bounds are relative to the containing display.
// So convert back to global coordinates if that screen still exists // So convert back to global coordinates if that screen still exists
if let WindowBounds::Fixed(mut window_bounds) = bounds { if let WindowBounds::Fixed(mut window_bounds) = bounds {
if let Some(screen) = cx.platform().screen_by_id(display) { if let Some(screen) = cx.platform().screen_by_id(display) {
let screen_bounds = screen.bounds(); let screen_bounds = screen.bounds();
window_bounds.set_origin_x( window_bounds.set_origin_x(
window_bounds.origin_x() + screen_bounds.origin_x(), window_bounds.origin_x() + screen_bounds.origin_x(),
); );
window_bounds.set_origin_y( window_bounds.set_origin_y(
window_bounds.origin_y() + screen_bounds.origin_y(), window_bounds.origin_y() + screen_bounds.origin_y(),
); );
bounds = WindowBounds::Fixed(window_bounds); bounds = WindowBounds::Fixed(window_bounds);
} else { } else {
// Screen no longer exists. Return none here. // Screen no longer exists. Return none here.
return None; return None;
}
} }
}
Some((bounds, display)) Some((bounds, display))
}) })
.unzip() .unzip()
}; };
// Use the serialized workspace to construct the new window // Use the serialized workspace to construct the new window
cx.add_window( cx.add_window(
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()), (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|cx| { |cx| {
Workspace::new( Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
workspace_id, },
project_handle.clone(), )
app_state.clone(), });
cx,
) // We haven't yielded the main thread since obtaining the window handle,
}, // so the window exists.
) let workspace = window.root(&cx).unwrap();
});
let workspace = window.root(&cx);
(app_state.initialize_workspace)( (app_state.initialize_workspace)(
workspace.downgrade(), workspace.downgrade(),
serialized_workspace.is_some(), serialized_workspace.is_some(),
@ -3985,7 +3977,7 @@ pub fn join_remote_project(
), ),
|cx| Workspace::new(0, project, app_state.clone(), cx), |cx| Workspace::new(0, project, app_state.clone(), cx),
); );
let workspace = window.root(&cx); let workspace = window.root(&cx).unwrap();
(app_state.initialize_workspace)( (app_state.initialize_workspace)(
workspace.downgrade(), workspace.downgrade(),
false, false,

View file

@ -985,7 +985,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -1566,7 +1566,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), cx)) .add_window(|cx| Workspace::test_new(project.clone(), cx))
.detach(cx); .root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
@ -1845,7 +1845,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx)) .add_window(|cx| Workspace::test_new(project, cx))
.detach(cx); .root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));