WIP: Return WindowHandle<V: View> from AppContext::add_window (#2820)

Instead of returning a usize for the window id, I'm instead returning a
`WindowHandle<V: View>` where `V` is the type of the window's root view.
@as-cii helped me with a cool technique using generic associated types
where methods on `WindowHandle` can return either T or Option<T>
depending on the `BorrowWindowContext::Result` associated type.

Some example usage...

```rs
let window = cx.add_window(|cx| MyView::new(cx));
let my_view = window.root(cx); // If cx is TestAppContext, returns MyView. Otherwise returns Option<MyView>, because the window could be closed.
```


This isn't insanely beneficial on its own, but I think it will help
clean up our testing story. I'm planning on making `window` more useful
in tests for laying out elements, etc.

- [x] Rework tests that call `add_window` 😅 to expect only a window in
return.
- [x] Get tests passing
- [x] 🚬  test
This commit is contained in:
Nathan Sobo 2023-08-03 18:45:51 -06:00 committed by GitHub
commit d3c1966d96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1174 additions and 896 deletions

View file

@ -495,8 +495,9 @@ impl TestClient {
// We use a workspace container so that we don't need to remove the window in order to // We use a workspace container so that we don't need to remove the window in order to
// drop the workspace and we can use a ViewHandle instead. // drop the workspace and we can use a ViewHandle instead.
let (window_id, container) = cx.add_window(|_| WorkspaceContainer { workspace: None }); let window = cx.add_window(|_| WorkspaceContainer { workspace: None });
let workspace = cx.add_view(window_id, |cx| Workspace::test_new(project.clone(), cx)); let container = window.root(cx);
let workspace = window.add_view(cx, |cx| Workspace::test_new(project.clone(), cx));
container.update(cx, |container, cx| { container.update(cx, |container, cx| {
container.workspace = Some(workspace.downgrade()); container.workspace = Some(workspace.downgrade());
cx.notify(); cx.notify();

View file

@ -7,8 +7,7 @@ use client::{User, RECEIVE_TIMEOUT};
use collections::HashSet; use collections::HashSet;
use editor::{ use editor::{
test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion, test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions, ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToggleCodeActions, Undo,
Undo,
}; };
use fs::{repository::GitFileStatus, FakeFs, Fs as _, LineEnding, RemoveOptions}; use fs::{repository::GitFileStatus, FakeFs, Fs as _, LineEnding, RemoveOptions};
use futures::StreamExt as _; use futures::StreamExt as _;
@ -1208,7 +1207,7 @@ async fn test_share_project(
cx_c: &mut TestAppContext, cx_c: &mut TestAppContext,
) { ) {
deterministic.forbid_parking(); deterministic.forbid_parking();
let (window_b, _) = cx_b.add_window(|_| EmptyView); let window_b = cx_b.add_window(|_| EmptyView);
let mut server = TestServer::start(&deterministic).await; let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await; let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await; let client_b = server.create_client(cx_b, "user_b").await;
@ -1316,7 +1315,7 @@ async fn test_share_project(
.await .await
.unwrap(); .unwrap();
let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx)); let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
// Client A sees client B's selection // Client A sees client B's selection
deterministic.run_until_parked(); deterministic.run_until_parked();
@ -1499,8 +1498,8 @@ async fn test_host_disconnect(
deterministic.run_until_parked(); deterministic.run_until_parked();
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
let (window_id_b, workspace_b) = let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let workspace_b = window_b.root(cx_b);
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "b.txt"), None, true, cx) workspace.open_path((worktree_id, "b.txt"), None, true, cx)
@ -1509,9 +1508,7 @@ async fn test_host_disconnect(
.unwrap() .unwrap()
.downcast::<Editor>() .downcast::<Editor>()
.unwrap(); .unwrap();
assert!(cx_b assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
.read_window(window_id_b, |cx| editor_b.is_focused(cx))
.unwrap());
editor_b.update(cx_b, |editor, cx| editor.insert("X", cx)); editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
assert!(cx_b.is_window_edited(workspace_b.window_id())); assert!(cx_b.is_window_edited(workspace_b.window_id()));
@ -1525,7 +1522,7 @@ async fn test_host_disconnect(
assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared())); assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
// Ensure client B's edited state is reset and that the whole window is blurred. // Ensure client B's edited state is reset and that the whole window is blurred.
cx_b.read_window(window_id_b, |cx| { window_b.read_with(cx_b, |cx| {
assert_eq!(cx.focused_view_id(), None); assert_eq!(cx.focused_view_id(), None);
}); });
assert!(!cx_b.is_window_edited(workspace_b.window_id())); assert!(!cx_b.is_window_edited(workspace_b.window_id()));
@ -3445,13 +3442,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
.await .await
.unwrap(); .unwrap();
let (window_a, _) = cx_a.add_window(|_| EmptyView); let window_a = cx_a.add_window(|_| EmptyView);
let editor_a = cx_a.add_view(window_a, |cx| { let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
Editor::for_buffer(buffer_a, Some(project_a), cx)
});
let mut editor_cx_a = EditorTestContext { let mut editor_cx_a = EditorTestContext {
cx: cx_a, cx: cx_a,
window_id: window_a, window_id: window_a.window_id(),
editor: editor_a, editor: editor_a,
}; };
@ -3460,13 +3455,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
.await .await
.unwrap(); .unwrap();
let (window_b, _) = cx_b.add_window(|_| EmptyView); let window_b = cx_b.add_window(|_| EmptyView);
let editor_b = cx_b.add_view(window_b, |cx| { let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
Editor::for_buffer(buffer_b, Some(project_b), cx)
});
let mut editor_cx_b = EditorTestContext { let mut editor_cx_b = EditorTestContext {
cx: cx_b, cx: cx_b,
window_id: window_b, window_id: window_b.window_id(),
editor: editor_b, editor: editor_b,
}; };
@ -4205,8 +4198,8 @@ async fn test_collaborating_with_completion(
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await .await
.unwrap(); .unwrap();
let (window_b, _) = cx_b.add_window(|_| EmptyView); let window_b = cx_b.add_window(|_| EmptyView);
let editor_b = cx_b.add_view(window_b, |cx| { let editor_b = window_b.add_view(cx_b, |cx| {
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx) Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
}); });
@ -5316,7 +5309,8 @@ async fn test_collaborating_with_code_actions(
// Join the project as client B. // Join the project as client B.
let project_b = client_b.build_remote_project(project_id, cx_b).await; let project_b = client_b.build_remote_project(project_id, cx_b).await;
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
let workspace_b = window_b.root(cx_b);
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), None, true, cx) workspace.open_path((worktree_id, "main.rs"), None, true, cx)
@ -5540,7 +5534,8 @@ async fn test_collaborating_with_renames(
.unwrap(); .unwrap();
let project_b = client_b.build_remote_project(project_id, cx_b).await; let project_b = client_b.build_remote_project(project_id, cx_b).await;
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
let workspace_b = window_b.root(cx_b);
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), None, true, cx) workspace.open_path((worktree_id, "one.rs"), None, true, cx)
@ -5571,6 +5566,7 @@ async fn test_collaborating_with_renames(
.unwrap(); .unwrap();
prepare_rename.await.unwrap(); prepare_rename.await.unwrap();
editor_b.update(cx_b, |editor, cx| { editor_b.update(cx_b, |editor, cx| {
use editor::ToOffset;
let rename = editor.pending_rename().unwrap(); let rename = editor.pending_rename().unwrap();
let buffer = editor.buffer().read(cx).snapshot(cx); let buffer = editor.buffer().read(cx).snapshot(cx);
assert_eq!( assert_eq!(
@ -7601,8 +7597,8 @@ async fn test_on_input_format_from_host_to_guest(
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await .await
.unwrap(); .unwrap();
let (window_a, _) = cx_a.add_window(|_| EmptyView); let window_a = cx_a.add_window(|_| EmptyView);
let editor_a = cx_a.add_view(window_a, |cx| { let editor_a = window_a.add_view(cx_a, |cx| {
Editor::for_buffer(buffer_a, Some(project_a.clone()), cx) Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
}); });
@ -7730,8 +7726,8 @@ async fn test_on_input_format_from_guest_to_host(
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await .await
.unwrap(); .unwrap();
let (window_b, _) = cx_b.add_window(|_| EmptyView); let window_b = cx_b.add_window(|_| EmptyView);
let editor_b = cx_b.add_view(window_b, |cx| { let editor_b = window_b.add_view(cx_b, |cx| {
Editor::for_buffer(buffer_b, Some(project_b.clone()), cx) Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
}); });

View file

@ -31,7 +31,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
for screen in cx.platform().screens() { for screen in cx.platform().screens() {
let screen_bounds = screen.bounds(); let screen_bounds = screen.bounds();
let (window_id, _) = cx.add_window( let window = cx.add_window(
WindowOptions { WindowOptions {
bounds: WindowBounds::Fixed(RectF::new( bounds: WindowBounds::Fixed(RectF::new(
screen_bounds.upper_right() screen_bounds.upper_right()
@ -49,7 +49,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()),
); );
notification_windows.push(window_id); notification_windows.push(window.window_id());
} }
} }
} }

View file

@ -26,7 +26,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
for screen in cx.platform().screens() { for screen in cx.platform().screens() {
let screen_bounds = screen.bounds(); let screen_bounds = screen.bounds();
let (window_id, _) = cx.add_window( let window = cx.add_window(
WindowOptions { WindowOptions {
bounds: WindowBounds::Fixed(RectF::new( bounds: WindowBounds::Fixed(RectF::new(
screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING), screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING),
@ -52,7 +52,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
notification_windows notification_windows
.entry(*project_id) .entry(*project_id)
.or_insert(Vec::new()) .or_insert(Vec::new())
.push(window_id); .push(window.window_id());
} }
} }
room::Event::RemoteProjectUnshared { project_id } => { room::Event::RemoteProjectUnshared { project_id } => {

View file

@ -295,7 +295,9 @@ mod tests {
let app_state = init_test(cx); let app_state = init_test(cx);
let project = Project::test(app_state.fs.clone(), [], cx).await; let project = Project::test(app_state.fs.clone(), [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let editor = cx.add_view(window_id, |cx| { let editor = cx.add_view(window_id, |cx| {
let mut editor = Editor::single_line(None, cx); let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx); editor.set_text("abc", cx);

View file

@ -4,7 +4,7 @@ use gpui::{
geometry::rect::RectF, geometry::rect::RectF,
platform::{WindowBounds, WindowKind, WindowOptions}, platform::{WindowBounds, WindowKind, WindowOptions},
AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext, AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext,
ViewHandle, WindowHandle,
}; };
use theme::ui::modal; use theme::ui::modal;
@ -18,43 +18,43 @@ const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot";
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
if let Some(copilot) = Copilot::global(cx) { if let Some(copilot) = Copilot::global(cx) {
let mut code_verification: Option<ViewHandle<CopilotCodeVerification>> = None; let mut verification_window: Option<WindowHandle<CopilotCodeVerification>> = None;
cx.observe(&copilot, move |copilot, cx| { cx.observe(&copilot, move |copilot, cx| {
let status = copilot.read(cx).status(); let status = copilot.read(cx).status();
match &status { match &status {
crate::Status::SigningIn { prompt } => { crate::Status::SigningIn { prompt } => {
if let Some(code_verification_handle) = code_verification.as_mut() { if let Some(window) = verification_window.as_mut() {
let window_id = code_verification_handle.window_id(); let updated = window
let updated = cx.update_window(window_id, |cx| { .root(cx)
code_verification_handle.update(cx, |code_verification, cx| { .map(|root| {
code_verification.set_status(status.clone(), cx) root.update(cx, |verification, cx| {
}); verification.set_status(status.clone(), cx);
cx.activate_window(); cx.activate_window();
}); })
if updated.is_none() { })
code_verification = Some(create_copilot_auth_window(cx, &status)); .is_some();
if !updated {
verification_window = Some(create_copilot_auth_window(cx, &status));
} }
} else if let Some(_prompt) = prompt { } else if let Some(_prompt) = prompt {
code_verification = Some(create_copilot_auth_window(cx, &status)); verification_window = Some(create_copilot_auth_window(cx, &status));
} }
} }
Status::Authorized | Status::Unauthorized => { Status::Authorized | Status::Unauthorized => {
if let Some(code_verification) = code_verification.as_ref() { if let Some(window) = verification_window.as_ref() {
let window_id = code_verification.window_id(); if let Some(verification) = window.root(cx) {
cx.update_window(window_id, |cx| { verification.update(cx, |verification, cx| {
code_verification.update(cx, |code_verification, cx| { verification.set_status(status, cx);
code_verification.set_status(status, cx)
});
cx.platform().activate(true); cx.platform().activate(true);
cx.activate_window(); cx.activate_window();
}); });
} }
} }
}
_ => { _ => {
if let Some(code_verification) = code_verification.take() { if let Some(code_verification) = verification_window.take() {
cx.update_window(code_verification.window_id(), |cx| cx.remove_window()); code_verification.update(cx, |cx| cx.remove_window());
} }
} }
} }
@ -66,7 +66,7 @@ pub fn init(cx: &mut AppContext) {
fn create_copilot_auth_window( fn create_copilot_auth_window(
cx: &mut AppContext, cx: &mut AppContext,
status: &Status, status: &Status,
) -> ViewHandle<CopilotCodeVerification> { ) -> WindowHandle<CopilotCodeVerification> {
let window_size = theme::current(cx).copilot.modal.dimensions(); let window_size = theme::current(cx).copilot.modal.dimensions();
let window_options = WindowOptions { let window_options = WindowOptions {
bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)), bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
@ -78,10 +78,9 @@ fn create_copilot_auth_window(
is_movable: true, is_movable: true,
screen: None, screen: None,
}; };
let (_, view) = cx.add_window(window_options, |_cx| { cx.add_window(window_options, |_cx| {
CopilotCodeVerification::new(status.clone()) CopilotCodeVerification::new(status.clone())
}); })
view
} }
pub struct CopilotCodeVerification { pub struct CopilotCodeVerification {

View file

@ -855,7 +855,9 @@ mod tests {
let language_server_id = LanguageServerId(0); let language_server_id = LanguageServerId(0);
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
// Create some diagnostics // Create some diagnostics
project.update(cx, |project, cx| { project.update(cx, |project, cx| {
@ -1248,7 +1250,9 @@ mod tests {
let server_id_1 = LanguageServerId(100); let server_id_1 = LanguageServerId(100);
let server_id_2 = LanguageServerId(101); let server_id_2 = LanguageServerId(101);
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let view = cx.add_view(window_id, |cx| { let view = cx.add_view(window_id, |cx| {
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)

View file

@ -48,7 +48,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
}); });
let events = Rc::new(RefCell::new(Vec::new())); let events = Rc::new(RefCell::new(Vec::new()));
let (_, editor1) = cx.add_window({ let editor1 = cx
.add_window({
let events = events.clone(); let events = events.clone();
|cx| { |cx| {
cx.subscribe(&cx.handle(), move |_, _, event, _| { cx.subscribe(&cx.handle(), move |_, _, event, _| {
@ -62,8 +63,10 @@ fn test_edit_events(cx: &mut TestAppContext) {
.detach(); .detach();
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, cx)
} }
}); })
let (_, editor2) = cx.add_window({ .root(cx);
let editor2 = cx
.add_window({
let events = events.clone(); let events = events.clone();
|cx| { |cx| {
cx.subscribe(&cx.handle(), move |_, _, event, _| { cx.subscribe(&cx.handle(), move |_, _, event, _| {
@ -77,7 +80,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
.detach(); .detach();
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, 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.
@ -173,7 +177,9 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval()); let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
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.clone(), cx)); let editor = cx
.add_window(|cx| build_editor(buffer.clone(), 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);
@ -343,10 +349,12 @@ fn test_ime_composition(cx: &mut TestAppContext) {
fn test_selection_with_mouse(cx: &mut TestAppContext) { fn test_selection_with_mouse(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.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);
}); });
@ -410,10 +418,12 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
fn test_canceling_pending_selection(cx: &mut TestAppContext) { fn test_canceling_pending_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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);
@ -456,10 +466,12 @@ fn test_clone(cx: &mut TestAppContext) {
true, true,
); );
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
let buffer = MultiBuffer::build_simple(&text, cx); let buffer = MultiBuffer::build_simple(&text, cx);
build_editor(buffer, cx) build_editor(buffer, 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()));
@ -473,9 +485,11 @@ fn test_clone(cx: &mut TestAppContext) {
); );
}); });
let (_, cloned_editor) = editor.update(cx, |editor, cx| { let cloned_editor = editor
.update(cx, |editor, cx| {
cx.add_window(Default::default(), |cx| editor.clone(cx)) cx.add_window(Default::default(), |cx| editor.clone(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));
@ -509,7 +523,9 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
cx.add_view(window_id, |cx| { cx.add_view(window_id, |cx| {
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
@ -618,10 +634,12 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
fn test_cancel(cx: &mut TestAppContext) { fn test_cancel(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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);
@ -661,7 +679,8 @@ fn test_cancel(cx: &mut TestAppContext) {
fn test_fold_action(cx: &mut TestAppContext) { fn test_fold_action(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
let buffer = MultiBuffer::build_simple( let buffer = MultiBuffer::build_simple(
&" &"
impl Foo { impl Foo {
@ -684,7 +703,8 @@ fn test_fold_action(cx: &mut TestAppContext) {
cx, cx,
); );
build_editor(buffer.clone(), cx) build_editor(buffer.clone(), 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| {
@ -752,7 +772,9 @@ fn test_move_cursor(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
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.add_window(|cx| build_editor(buffer.clone(), cx)); let view = cx
.add_window(|cx| build_editor(buffer.clone(), cx))
.root(cx);
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
@ -827,10 +849,12 @@ fn test_move_cursor(cx: &mut TestAppContext) {
fn test_move_cursor_multibyte(cx: &mut TestAppContext) { fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
assert_eq!('ⓐ'.len_utf8(), 3); assert_eq!('ⓐ'.len_utf8(), 3);
assert_eq!('α'.len_utf8(), 2); assert_eq!('α'.len_utf8(), 2);
@ -932,10 +956,12 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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())]);
@ -982,10 +1008,12 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
fn test_beginning_end_of_line(cx: &mut TestAppContext) { fn test_beginning_end_of_line(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -1145,10 +1173,12 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
fn test_prev_next_word_boundary(cx: &mut TestAppContext) { fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -1197,10 +1227,13 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) { fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); .add_window(|cx| {
let buffer =
MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
build_editor(buffer, cx) build_editor(buffer, 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);
@ -1530,10 +1563,12 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
fn test_delete_to_word_boundary(cx: &mut TestAppContext) { fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -1566,10 +1601,12 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
fn test_newline(cx: &mut TestAppContext) { fn test_newline(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -1589,7 +1626,8 @@ fn test_newline(cx: &mut TestAppContext) {
fn test_newline_with_old_selections(cx: &mut TestAppContext) { fn test_newline_with_old_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
let buffer = MultiBuffer::build_simple( let buffer = MultiBuffer::build_simple(
" "
a a
@ -1612,7 +1650,8 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
]) ])
}); });
editor editor
}); })
.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
@ -1817,12 +1856,14 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
fn test_insert_with_old_selections(cx: &mut TestAppContext) { fn test_insert_with_old_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
let mut editor = build_editor(buffer.clone(), cx); let mut editor = build_editor(buffer.clone(), cx);
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
}); })
.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
@ -2329,10 +2370,12 @@ async fn test_delete(cx: &mut gpui::TestAppContext) {
fn test_delete_line(cx: &mut TestAppContext) { fn test_delete_line(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -2352,10 +2395,12 @@ fn test_delete_line(cx: &mut TestAppContext) {
); );
}); });
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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)])
@ -2654,10 +2699,12 @@ async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
fn test_duplicate_line(cx: &mut TestAppContext) { fn test_duplicate_line(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -2680,10 +2727,12 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
); );
}); });
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -2707,10 +2756,12 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
fn test_move_line_up_down(cx: &mut TestAppContext) { fn test_move_line_up_down(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.fold_ranges( view.fold_ranges(
vec![ vec![
@ -2806,10 +2857,12 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.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(
@ -2834,8 +2887,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
fn test_transpose(cx: &mut TestAppContext) { fn test_transpose(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
_ = cx _ = cx.add_window(|cx| {
.add_window(|cx| {
let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
editor.change_selections(None, cx, |s| s.select_ranges([1..1])); editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
@ -2852,11 +2904,9 @@ fn test_transpose(cx: &mut TestAppContext) {
assert_eq!(editor.selections.ranges(cx), [3..3]); assert_eq!(editor.selections.ranges(cx), [3..3]);
editor editor
}) });
.1;
_ = cx _ = cx.add_window(|cx| {
.add_window(|cx| {
let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
@ -2878,11 +2928,9 @@ fn test_transpose(cx: &mut TestAppContext) {
assert_eq!(editor.selections.ranges(cx), [6..6]); assert_eq!(editor.selections.ranges(cx), [6..6]);
editor editor
}) });
.1;
_ = cx _ = cx.add_window(|cx| {
.add_window(|cx| {
let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
@ -2907,11 +2955,9 @@ fn test_transpose(cx: &mut TestAppContext) {
assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
editor editor
}) });
.1;
_ = cx _ = cx.add_window(|cx| {
.add_window(|cx| {
let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
@ -2928,8 +2974,7 @@ fn test_transpose(cx: &mut TestAppContext) {
assert_eq!(editor.selections.ranges(cx), [11..11]); assert_eq!(editor.selections.ranges(cx), [11..11]);
editor editor
}) });
.1;
} }
#[gpui::test] #[gpui::test]
@ -3132,10 +3177,12 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
fn test_select_all(cx: &mut TestAppContext) { fn test_select_all(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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!(
@ -3149,10 +3196,12 @@ fn test_select_all(cx: &mut TestAppContext) {
fn test_select_line(cx: &mut TestAppContext) { fn test_select_line(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.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([
@ -3196,10 +3245,12 @@ fn test_select_line(cx: &mut TestAppContext) {
fn test_split_selection_into_lines(cx: &mut TestAppContext) { fn test_split_selection_into_lines(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.fold_ranges( view.fold_ranges(
vec![ vec![
@ -3267,10 +3318,12 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
fn test_add_selection_above_below(cx: &mut TestAppContext) { fn test_add_selection_above_below(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, view) = cx.add_window(|cx| { let view = cx
.add_window(|cx| {
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)
}); })
.root(cx);
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| { view.change_selections(None, cx, |s| {
@ -3555,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)); 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;
@ -3718,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)); 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;
@ -4281,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)); 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;
@ -4429,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)); 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;
@ -4519,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)); 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();
@ -4649,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)); 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)));
@ -4761,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)); 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)));
@ -4875,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)); 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| {
@ -5653,7 +5706,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let (_, view) = cx.add_window(|cx| build_editor(multibuffer, 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| {
@ -5723,7 +5776,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let (_, view) = cx.add_window(|cx| build_editor(multibuffer, 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! {"
@ -5799,7 +5852,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
let mut editor = build_editor(multibuffer.clone(), cx); let mut editor = build_editor(multibuffer.clone(), cx);
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
editor.change_selections(None, cx, |s| { editor.change_selections(None, cx, |s| {
@ -5814,7 +5868,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
] ]
); );
editor editor
}); })
.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| {
@ -5884,7 +5939,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
multibuffer multibuffer
}); });
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
let mut editor = build_editor(multibuffer.clone(), cx); let mut editor = build_editor(multibuffer.clone(), cx);
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx); editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
@ -5893,7 +5949,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
[Point::new(1, 3)..Point::new(1, 3)] [Point::new(1, 3)..Point::new(1, 3)]
); );
editor editor
}); })
.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);
@ -5956,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)); 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;
@ -5992,10 +6049,12 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
fn test_highlighted_ranges(cx: &mut TestAppContext) { fn test_highlighted_ranges(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
struct Type1; struct Type1;
@ -6084,8 +6143,11 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
.unwrap(); .unwrap();
cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)) cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
}); });
let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx)); let leader = cx
let (_, follower) = cx.update(|cx| { .add_window(|cx| build_editor(buffer.clone(), cx))
.root(cx);
let follower = cx
.update(|cx| {
cx.add_window( cx.add_window(
WindowOptions { WindowOptions {
bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))), bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
@ -6093,7 +6155,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
}, },
|cx| build_editor(buffer.clone(), cx), |cx| build_editor(buffer.clone(), 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));
@ -6224,7 +6287,9 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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| {
@ -6968,7 +7033,7 @@ async fn test_copilot_multibuffer(
); );
multibuffer multibuffer
}); });
let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, 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,
@ -7098,7 +7163,7 @@ async fn test_copilot_disabled_globs(
); );
multibuffer multibuffer
}); });
let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, 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 {
@ -7177,7 +7242,9 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
.await; .await;
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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()
@ -7282,7 +7349,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
.await; .await;
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let _buffer = project let _buffer = project
.update(cx, |project, cx| { .update(cx, |project, cx| {
project.open_local_buffer("/a/main.rs", cx) project.open_local_buffer("/a/main.rs", cx)

View file

@ -3002,10 +3002,12 @@ mod tests {
fn test_layout_line_numbers(cx: &mut TestAppContext) { fn test_layout_line_numbers(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.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| {
@ -3021,10 +3023,12 @@ mod tests {
fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.root(cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.set_placeholder_text("hello", cx); editor.set_placeholder_text("hello", cx);
@ -3231,10 +3235,12 @@ mod tests {
info!( info!(
"Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
); );
let (_, editor) = cx.add_window(|cx| { let editor = cx
.add_window(|cx| {
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)
}); })
.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

@ -1135,7 +1135,9 @@ mod tests {
) )
.await; .await;
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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()
@ -1835,7 +1837,9 @@ mod tests {
.await; .await;
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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()
@ -1988,7 +1992,9 @@ mod tests {
project.update(cx, |project, _| { project.update(cx, |project, _| {
project.languages().add(Arc::clone(&language)) project.languages().add(Arc::clone(&language))
}); });
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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()
@ -2074,8 +2080,9 @@ mod tests {
deterministic.run_until_parked(); deterministic.run_until_parked();
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let (_, editor) = let editor = cx
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)); .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), 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);
@ -2327,7 +2334,9 @@ all hints should be invalidated and requeried for all of its visible excerpts"
project.update(cx, |project, _| { project.update(cx, |project, _| {
project.languages().add(Arc::clone(&language)) project.languages().add(Arc::clone(&language))
}); });
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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()
@ -2372,8 +2381,9 @@ all hints should be invalidated and requeried for all of its visible excerpts"
deterministic.run_until_parked(); deterministic.run_until_parked();
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let (_, editor) = let editor = cx
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)); .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), 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);
@ -2561,7 +2571,9 @@ all hints should be invalidated and requeried for all of its visible excerpts"
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language))); project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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

@ -69,7 +69,8 @@ impl<'a> EditorLspTestContext<'a> {
.insert_tree("/root", json!({ "dir": { file_name.clone(): "" }})) .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
.await; .await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
project project
.update(cx, |project, cx| { .update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx) project.find_or_create_local_worktree("/root", true, cx)
@ -98,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> {
Self { Self {
cx: EditorTestContext { cx: EditorTestContext {
cx, cx,
window_id, window_id: window.window_id(),
editor, editor,
}, },
lsp, lsp,

View file

@ -32,16 +32,14 @@ impl<'a> EditorTestContext<'a> {
let buffer = project let buffer = project
.update(cx, |project, cx| project.create_buffer("", None, cx)) .update(cx, |project, cx| project.create_buffer("", None, cx))
.unwrap(); .unwrap();
let (window_id, editor) = cx.update(|cx| { let window = cx.add_window(|cx| {
cx.add_window(Default::default(), |cx| {
cx.focus_self(); cx.focus_self();
build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx) build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx)
})
}); });
let editor = window.root(cx);
Self { Self {
cx, cx,
window_id, window_id: window.window_id(),
editor, editor,
} }
} }

View file

@ -617,8 +617,9 @@ mod tests {
.await; .await;
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 (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
cx.dispatch_action(window_id, Toggle); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
finder finder
@ -631,8 +632,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window_id, SelectNext); cx.dispatch_action(window.window_id(), SelectNext);
cx.dispatch_action(window_id, Confirm); cx.dispatch_action(window.window_id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -671,8 +672,9 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
cx.dispatch_action(window_id, Toggle); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
let file_query = &first_file_name[..3]; let file_query = &first_file_name[..3];
@ -704,8 +706,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window_id, SelectNext); cx.dispatch_action(window.window_id(), SelectNext);
cx.dispatch_action(window_id, Confirm); cx.dispatch_action(window.window_id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -754,8 +756,9 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
cx.dispatch_action(window_id, Toggle); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
let file_query = &first_file_name[..3]; let file_query = &first_file_name[..3];
@ -787,8 +790,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window_id, SelectNext); cx.dispatch_action(window.window_id(), SelectNext);
cx.dispatch_action(window_id, Confirm); cx.dispatch_action(window.window_id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -837,8 +840,11 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
let (_, finder) = cx.add_window(|cx| { .add_window(|cx| Workspace::test_new(project, cx))
.root(cx);
let finder = cx
.add_window(|cx| {
Picker::new( Picker::new(
FileFinderDelegate::new( FileFinderDelegate::new(
workspace.downgrade(), workspace.downgrade(),
@ -849,7 +855,8 @@ mod tests {
), ),
cx, cx,
) )
}); })
.root(cx);
let query = test_path_like("hi"); let query = test_path_like("hi");
finder finder
@ -931,8 +938,11 @@ mod tests {
cx, cx,
) )
.await; .await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
let (_, finder) = cx.add_window(|cx| { .add_window(|cx| Workspace::test_new(project, cx))
.root(cx);
let finder = cx
.add_window(|cx| {
Picker::new( Picker::new(
FileFinderDelegate::new( FileFinderDelegate::new(
workspace.downgrade(), workspace.downgrade(),
@ -943,7 +953,8 @@ mod tests {
), ),
cx, 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)
@ -967,8 +978,11 @@ mod tests {
cx, cx,
) )
.await; .await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
let (_, finder) = cx.add_window(|cx| { .add_window(|cx| Workspace::test_new(project, cx))
.root(cx);
let finder = cx
.add_window(|cx| {
Picker::new( Picker::new(
FileFinderDelegate::new( FileFinderDelegate::new(
workspace.downgrade(), workspace.downgrade(),
@ -979,7 +993,8 @@ mod tests {
), ),
cx, 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.
@ -1015,61 +1030,6 @@ mod tests {
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0)); finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
} }
#[gpui::test]
async fn test_multiple_matches_with_same_relative_path(cx: &mut TestAppContext) {
let app_state = init_test(cx);
app_state
.fs
.as_fake()
.insert_tree(
"/root",
json!({
"dir1": { "a.txt": "" },
"dir2": { "a.txt": "" }
}),
)
.await;
let project = Project::test(
app_state.fs.clone(),
["/root/dir1".as_ref(), "/root/dir2".as_ref()],
cx,
)
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
let (_, finder) = cx.add_window(|cx| {
Picker::new(
FileFinderDelegate::new(
workspace.downgrade(),
workspace.read(cx).project().clone(),
None,
Vec::new(),
cx,
),
cx,
)
});
// Run a search that matches two files with the same relative path.
finder
.update(cx, |f, cx| {
f.delegate_mut().spawn_search(test_path_like("a.t"), cx)
})
.await;
// Can switch between different matches with the same relative path.
finder.update(cx, |finder, cx| {
let delegate = finder.delegate_mut();
assert_eq!(delegate.matches.len(), 2);
assert_eq!(delegate.selected_index(), 0);
delegate.set_selected_index(1, cx);
assert_eq!(delegate.selected_index(), 1);
delegate.set_selected_index(0, cx);
assert_eq!(delegate.selected_index(), 0);
});
}
#[gpui::test] #[gpui::test]
async fn test_path_distance_ordering(cx: &mut TestAppContext) { async fn test_path_distance_ordering(cx: &mut TestAppContext) {
let app_state = init_test(cx); let app_state = init_test(cx);
@ -1089,7 +1049,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project, 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);
@ -1103,7 +1065,8 @@ mod tests {
worktree_id, worktree_id,
path: Arc::from(Path::new("/root/dir2/b.txt")), path: Arc::from(Path::new("/root/dir2/b.txt")),
})); }));
let (_, finder) = cx.add_window(|cx| { let finder = cx
.add_window(|cx| {
Picker::new( Picker::new(
FileFinderDelegate::new( FileFinderDelegate::new(
workspace.downgrade(), workspace.downgrade(),
@ -1114,7 +1077,8 @@ mod tests {
), ),
cx, cx,
) )
}); })
.root(cx);
finder finder
.update(cx, |f, cx| { .update(cx, |f, cx| {
@ -1151,8 +1115,11 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
let (_, finder) = cx.add_window(|cx| { .add_window(|cx| Workspace::test_new(project, cx))
.root(cx);
let finder = cx
.add_window(|cx| {
Picker::new( Picker::new(
FileFinderDelegate::new( FileFinderDelegate::new(
workspace.downgrade(), workspace.downgrade(),
@ -1163,7 +1130,8 @@ mod tests {
), ),
cx, 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)
@ -1198,7 +1166,9 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
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);
@ -1404,7 +1374,9 @@ mod tests {
.detach(); .detach();
deterministic.run_until_parked(); deterministic.run_until_parked();
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
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,);

View file

@ -130,8 +130,14 @@ pub trait BorrowAppContext {
} }
pub trait BorrowWindowContext { pub trait BorrowWindowContext {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T; type Result<T>;
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T;
fn read_window_with<T, F>(&self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&WindowContext) -> T;
fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T>
where
F: FnOnce(&mut WindowContext) -> T;
} }
#[derive(Clone)] #[derive(Clone)]
@ -402,7 +408,7 @@ impl AsyncAppContext {
&mut self, &mut self,
window_options: WindowOptions, window_options: WindowOptions,
build_root_view: F, build_root_view: F,
) -> (usize, ViewHandle<T>) ) -> WindowHandle<T>
where where
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> T, F: FnOnce(&mut ViewContext<T>) -> T,
@ -452,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);
@ -494,8 +520,8 @@ pub struct AppContext {
// Action Types -> Action Handlers // Action Types -> Action Handlers
global_actions: HashMap<TypeId, Box<GlobalActionCallback>>, global_actions: HashMap<TypeId, Box<GlobalActionCallback>>,
keystroke_matcher: KeymapMatcher, keystroke_matcher: KeymapMatcher,
next_entity_id: usize, next_id: usize,
next_window_id: usize, // next_window_id: usize,
next_subscription_id: usize, next_subscription_id: usize,
frame_count: usize, frame_count: usize,
@ -554,8 +580,7 @@ impl AppContext {
actions: Default::default(), actions: Default::default(),
global_actions: Default::default(), global_actions: Default::default(),
keystroke_matcher: KeymapMatcher::default(), keystroke_matcher: KeymapMatcher::default(),
next_entity_id: 0, next_id: 0,
next_window_id: 0,
next_subscription_id: 0, next_subscription_id: 0,
frame_count: 0, frame_count: 0,
subscriptions: Default::default(), subscriptions: Default::default(),
@ -783,7 +808,7 @@ impl AppContext {
result result
} }
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>( fn read_window<T, F: FnOnce(&WindowContext) -> T>(
&self, &self,
window_id: usize, window_id: usize,
callback: F, callback: F,
@ -1226,7 +1251,7 @@ impl AppContext {
F: FnOnce(&mut ModelContext<T>) -> T, F: FnOnce(&mut ModelContext<T>) -> T,
{ {
self.update(|this| { self.update(|this| {
let model_id = post_inc(&mut this.next_entity_id); let model_id = post_inc(&mut this.next_id);
let handle = ModelHandle::new(model_id, &this.ref_counts); let handle = ModelHandle::new(model_id, &this.ref_counts);
let mut cx = ModelContext::new(this, model_id); let mut cx = ModelContext::new(this, model_id);
let model = build_model(&mut cx); let model = build_model(&mut cx);
@ -1300,20 +1325,19 @@ impl AppContext {
&mut self, &mut self,
window_options: WindowOptions, window_options: WindowOptions,
build_root_view: F, build_root_view: F,
) -> (usize, ViewHandle<V>) ) -> WindowHandle<V>
where where
V: View, V: View,
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
self.update(|this| { self.update(|this| {
let window_id = post_inc(&mut this.next_window_id); let window_id = post_inc(&mut this.next_id);
let platform_window = let platform_window =
this.platform this.platform
.open_window(window_id, window_options, this.foreground.clone()); .open_window(window_id, window_options, this.foreground.clone());
let window = this.build_window(window_id, platform_window, build_root_view); let window = this.build_window(window_id, platform_window, build_root_view);
let root_view = window.root_view().clone().downcast::<V>().unwrap();
this.windows.insert(window_id, window); this.windows.insert(window_id, window);
(window_id, root_view) WindowHandle::new(window_id)
}) })
} }
@ -1323,7 +1347,7 @@ impl AppContext {
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
self.update(|this| { self.update(|this| {
let window_id = post_inc(&mut this.next_window_id); let window_id = post_inc(&mut this.next_id);
let platform_window = this.platform.add_status_item(window_id); let platform_window = this.platform.add_status_item(window_id);
let window = this.build_window(window_id, platform_window, build_root_view); let window = this.build_window(window_id, platform_window, build_root_view);
let root_view = window.root_view().clone().downcast::<V>().unwrap(); let root_view = window.root_view().clone().downcast::<V>().unwrap();
@ -1422,7 +1446,7 @@ impl AppContext {
&mut self, &mut self,
window_id: usize, window_id: usize,
build_root_view: F, build_root_view: F,
) -> Option<ViewHandle<V>> ) -> Option<WindowHandle<V>>
where where
V: View, V: View,
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
@ -2158,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),
@ -3356,12 +3398,18 @@ impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
} }
impl<V> BorrowWindowContext for ViewContext<'_, '_, V> { impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { type Result<T> = T;
BorrowWindowContext::read_with(&*self.window_context, window_id, f)
fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
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)
} }
} }
@ -3461,12 +3509,18 @@ impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
} }
impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> { impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { type Result<T> = T;
BorrowWindowContext::read_with(&*self.view_context, window_id, f)
fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
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)
} }
} }
@ -3513,12 +3567,18 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
} }
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> { impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { type Result<T> = T;
BorrowWindowContext::read_with(&*self.view_context, window_id, f)
fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
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)
} }
} }
@ -3802,6 +3862,89 @@ impl<T> Clone for WeakModelHandle<T> {
impl<T> Copy for WeakModelHandle<T> {} impl<T> Copy for WeakModelHandle<T> {}
pub struct WindowHandle<T> {
window_id: usize,
root_view_type: PhantomData<T>,
}
#[allow(dead_code)]
impl<V: View> WindowHandle<V> {
fn new(window_id: usize) -> Self {
WindowHandle {
window_id,
root_view_type: PhantomData,
}
}
pub fn window_id(&self) -> usize {
self.window_id
}
pub fn root<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<ViewHandle<V>> {
self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap())
}
pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&WindowContext) -> R,
{
cx.read_window_with(self.window_id(), |cx| read(cx))
}
pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&mut WindowContext) -> R,
{
cx.update_window(self.window_id(), update)
}
// pub fn update_root<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<Option<R>>
// where
// C: BorrowWindowContext,
// F: FnOnce(&mut V, &mut ViewContext<V>) -> R,
// {
// cx.update_window(self.window_id, |cx| {
// cx.root_view()
// .clone()
// .downcast::<V>()
// .map(|v| v.update(cx, update))
// })
// }
pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V {
let root_view = cx
.read_window(self.window_id(), |cx| {
cx.root_view().clone().downcast().unwrap()
})
.unwrap();
root_view.read(cx)
}
pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&V, &ViewContext<V>) -> R,
{
self.read_with(cx, |cx| {
cx.root_view()
.downcast_ref::<V>()
.unwrap()
.read_with(cx, read)
})
}
pub fn add_view<C, U, F>(&self, cx: &mut C, build_view: F) -> C::Result<ViewHandle<U>>
where
C: BorrowWindowContext,
U: View,
F: FnOnce(&mut ViewContext<U>) -> U,
{
self.update(cx, |cx| cx.add_view(build_view))
}
}
#[repr(transparent)] #[repr(transparent)]
pub struct ViewHandle<T> { pub struct ViewHandle<T> {
any_handle: AnyViewHandle, any_handle: AnyViewHandle,
@ -3849,25 +3992,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) -> 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) -> 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)
@ -4684,11 +4827,11 @@ mod tests {
} }
} }
let (_, view) = cx.add_window(|_| View { render_count: 0 }); let window = cx.add_window(|_| View { render_count: 0 });
let called_defer = Rc::new(AtomicBool::new(false)); let called_defer = Rc::new(AtomicBool::new(false));
let called_after_window_update = Rc::new(AtomicBool::new(false)); let called_after_window_update = Rc::new(AtomicBool::new(false));
view.update(cx, |this, cx| { window.root(cx).update(cx, |this, cx| {
assert_eq!(this.render_count, 1); assert_eq!(this.render_count, 1);
cx.defer({ cx.defer({
let called_defer = called_defer.clone(); let called_defer = called_defer.clone();
@ -4712,7 +4855,7 @@ mod tests {
assert!(called_defer.load(SeqCst)); assert!(called_defer.load(SeqCst));
assert!(called_after_window_update.load(SeqCst)); assert!(called_after_window_update.load(SeqCst));
assert_eq!(view.read_with(cx, |view, _| view.render_count), 3); assert_eq!(window.read_root_with(cx, |view, _| view.render_count), 3);
} }
#[crate::test(self)] #[crate::test(self)]
@ -4751,9 +4894,9 @@ mod tests {
} }
} }
let (window_id, _root_view) = cx.add_window(|cx| View::new(None, cx)); let window = cx.add_window(|cx| View::new(None, cx));
let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx)); let handle_1 = window.add_view(cx, |cx| View::new(None, cx));
let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx)); let handle_2 = window.add_view(cx, |cx| View::new(Some(handle_1.clone()), cx));
assert_eq!(cx.read(|cx| cx.views.len()), 3); assert_eq!(cx.read(|cx| cx.views.len()), 3);
handle_1.update(cx, |view, cx| { handle_1.update(cx, |view, cx| {
@ -4813,11 +4956,11 @@ mod tests {
} }
let mouse_down_count = Arc::new(AtomicUsize::new(0)); let mouse_down_count = Arc::new(AtomicUsize::new(0));
let (window_id, _) = cx.add_window(Default::default(), |_| View { let window = cx.add_window(Default::default(), |_| View {
mouse_down_count: mouse_down_count.clone(), mouse_down_count: mouse_down_count.clone(),
}); });
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
// Ensure window's root element is in a valid lifecycle state. // Ensure window's root element is in a valid lifecycle state.
cx.dispatch_event( cx.dispatch_event(
Event::MouseDown(MouseButtonEvent { Event::MouseDown(MouseButtonEvent {
@ -4833,7 +4976,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>>,
} }
@ -4876,12 +5019,15 @@ mod tests {
let model = cx.add_model(|_| Model { let model = cx.add_model(|_| Model {
released: model_released.clone(), released: model_released.clone(),
}); });
let (window_id, view) = 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);
assert!(!model_released.get()); assert!(!model_released.get());
assert!(!view_released.get()); assert!(!view_released.get());
cx.update(|cx| {
cx.observe_release(&model, { cx.observe_release(&model, {
let model_release_observed = model_release_observed.clone(); let model_release_observed = model_release_observed.clone();
move |_, _| model_release_observed.set(true) move |_, _| model_release_observed.set(true)
@ -4892,6 +5038,7 @@ mod tests {
move |_, _| view_release_observed.set(true) move |_, _| view_release_observed.set(true)
}) })
.detach(); .detach();
});
cx.update(move |_| { cx.update(move |_| {
drop(model); drop(model);
@ -4900,7 +5047,7 @@ mod tests {
assert!(model_release_observed.get()); assert!(model_release_observed.get());
drop(view); drop(view);
cx.update_window(window_id, |cx| cx.remove_window()); window.update(cx, |cx| cx.remove_window());
assert!(view_released.get()); assert!(view_released.get());
assert!(view_release_observed.get()); assert!(view_release_observed.get());
} }
@ -4913,8 +5060,9 @@ mod tests {
type Event = String; type Event = String;
} }
let (window_id, handle_1) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let handle_2 = cx.add_view(window_id, |_| TestView::default()); let handle_1 = window.root(cx);
let handle_2 = window.add_view(cx, |_| TestView::default());
let handle_3 = cx.add_model(|_| Model); let handle_3 = cx.add_model(|_| Model);
handle_1.update(cx, |_, cx| { handle_1.update(cx, |_, cx| {
@ -5140,9 +5288,9 @@ mod tests {
type Event = (); type Event = ();
} }
let (window_id, _root_view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let observing_view = cx.add_view(window_id, |_| TestView::default()); let observing_view = window.add_view(cx, |_| TestView::default());
let emitting_view = cx.add_view(window_id, |_| TestView::default()); let emitting_view = window.add_view(cx, |_| TestView::default());
let observing_model = cx.add_model(|_| Model); let observing_model = cx.add_model(|_| Model);
let observed_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model);
@ -5165,7 +5313,7 @@ mod tests {
#[crate::test(self)] #[crate::test(self)]
fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) { fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) {
let (_, view) = cx.add_window::<TestView, _>(Default::default(), |cx| { let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
drop(cx.subscribe(&cx.handle(), { drop(cx.subscribe(&cx.handle(), {
move |this, _, _, _| this.events.push("dropped before flush".into()) move |this, _, _, _| this.events.push("dropped before flush".into())
})); }));
@ -5181,7 +5329,7 @@ mod tests {
TestView { events: Vec::new() } TestView { events: Vec::new() }
}); });
assert_eq!(view.read(cx).events, ["before emit"]); assert_eq!(window.read_root(cx).events, ["before emit"]);
} }
#[crate::test(self)] #[crate::test(self)]
@ -5195,7 +5343,8 @@ mod tests {
type Event = (); type Event = ();
} }
let (_, view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let view = window.root(cx);
let model = cx.add_model(|_| Model { let model = cx.add_model(|_| Model {
state: "old-state".into(), state: "old-state".into(),
}); });
@ -5216,7 +5365,7 @@ mod tests {
#[crate::test(self)] #[crate::test(self)]
fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) { fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) {
let (_, view) = cx.add_window::<TestView, _>(Default::default(), |cx| { let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
drop(cx.observe(&cx.handle(), { drop(cx.observe(&cx.handle(), {
move |this, _, _| this.events.push("dropped before flush".into()) move |this, _, _| this.events.push("dropped before flush".into())
})); }));
@ -5232,7 +5381,7 @@ mod tests {
TestView { events: Vec::new() } TestView { events: Vec::new() }
}); });
assert_eq!(view.read(cx).events, ["before notify"]); assert_eq!(window.read_root(cx).events, ["before notify"]);
} }
#[crate::test(self)] #[crate::test(self)]
@ -5243,7 +5392,8 @@ mod tests {
} }
let model = cx.add_model(|_| Model); let model = cx.add_model(|_| Model);
let (_, view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let view = window.root(cx);
view.update(cx, |_, cx| { view.update(cx, |_, cx| {
model.update(cx, |_, cx| cx.notify()); model.update(cx, |_, cx| cx.notify());
@ -5267,8 +5417,8 @@ mod tests {
type Event = (); type Event = ();
} }
let (window_id, _root_view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let observing_view = cx.add_view(window_id, |_| TestView::default()); let observing_view = window.add_view(cx, |_| TestView::default());
let observing_model = cx.add_model(|_| Model); let observing_model = cx.add_model(|_| Model);
let observed_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model);
@ -5390,9 +5540,9 @@ mod tests {
} }
} }
let (window_id, _root_view) = cx.add_window(|_| View); let window = cx.add_window(|_| View);
let observing_view = cx.add_view(window_id, |_| View); let observing_view = window.add_view(cx, |_| View);
let observed_view = cx.add_view(window_id, |_| View); let observed_view = window.add_view(cx, |_| View);
let observation_count = Rc::new(RefCell::new(0)); let observation_count = Rc::new(RefCell::new(0));
observing_view.update(cx, |_, cx| { observing_view.update(cx, |_, cx| {
@ -5474,13 +5624,13 @@ mod tests {
} }
let view_events: Arc<Mutex<Vec<String>>> = Default::default(); let view_events: Arc<Mutex<Vec<String>>> = Default::default();
let (window_id, view_1) = cx.add_window(|_| View { let window = cx.add_window(|_| View {
events: view_events.clone(), events: view_events.clone(),
name: "view 1".to_string(), name: "view 1".to_string(),
child: None, child: None,
}); });
let view_2 = cx let view_1 = window.root(cx);
.update_window(window_id, |cx| { let view_2 = window.update(cx, |cx| {
let view_2 = cx.add_view(|_| View { let view_2 = cx.add_view(|_| View {
events: view_events.clone(), events: view_events.clone(),
name: "view 2".to_string(), name: "view 2".to_string(),
@ -5491,8 +5641,7 @@ mod tests {
cx.notify(); cx.notify();
}); });
view_2 view_2
}) });
.unwrap();
let observed_events: Arc<Mutex<Vec<String>>> = Default::default(); let observed_events: Arc<Mutex<Vec<String>>> = Default::default();
view_1.update(cx, |_, cx| { view_1.update(cx, |_, cx| {
@ -5619,7 +5768,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>,
@ -5670,7 +5819,9 @@ 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()));
let observed_actions = Rc::new(RefCell::new(Vec::new()));
cx.update(|cx| {
cx.add_global_action({ cx.add_global_action({
let actions = actions.clone(); let actions = actions.clone();
move |_: &Action, _: &mut AppContext| { move |_: &Action, _: &mut AppContext| {
@ -5724,47 +5875,41 @@ mod tests {
} }
}); });
let observed_actions = Rc::new(RefCell::new(Vec::new()));
cx.observe_actions({ cx.observe_actions({
let observed_actions = observed_actions.clone(); let observed_actions = observed_actions.clone();
move |action_id, _| observed_actions.borrow_mut().push(action_id) move |action_id, _| observed_actions.borrow_mut().push(action_id)
}) })
.detach(); .detach();
});
let (window_id, view_1) = let window = cx.add_window(|_| ViewA { id: 1, child: None });
cx.add_window(Default::default(), |_| ViewA { id: 1, child: None }); let view_1 = window.root(cx);
let view_2 = cx let view_2 = window.update(cx, |cx| {
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 2, child: None }); let child = cx.add_view(|_| ViewB { id: 2, child: None });
view_1.update(cx, |view, cx| { view_1.update(cx, |view, cx| {
view.child = Some(child.clone().into_any()); view.child = Some(child.clone().into_any());
cx.notify(); cx.notify();
}); });
child child
}) });
.unwrap(); let view_3 = window.update(cx, |cx| {
let view_3 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None }); let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| { view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any()); view.child = Some(child.clone().into_any());
cx.notify(); cx.notify();
}); });
child child
}) });
.unwrap(); let view_4 = window.update(cx, |cx| {
let view_4 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None }); let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| { view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any()); view.child = Some(child.clone().into_any());
cx.notify(); cx.notify();
}); });
child child
}) });
.unwrap();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string())) cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
}); });
@ -5786,31 +5931,27 @@ mod tests {
// Remove view_1, which doesn't propagate the action // Remove view_1, which doesn't propagate the action
let (window_id, view_2) = let window = cx.add_window(|_| ViewB { id: 2, child: None });
cx.add_window(Default::default(), |_| ViewB { id: 2, child: None }); let view_2 = window.root(cx);
let view_3 = cx let view_3 = window.update(cx, |cx| {
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None }); let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| { view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any()); view.child = Some(child.clone().into_any());
cx.notify(); cx.notify();
}); });
child child
}) });
.unwrap(); let view_4 = window.update(cx, |cx| {
let view_4 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None }); let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| { view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any()); view.child = Some(child.clone().into_any());
cx.notify(); cx.notify();
}); });
child child
}) });
.unwrap();
actions.borrow_mut().clear(); actions.borrow_mut().clear();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string())) cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
}); });
@ -5887,7 +6028,7 @@ mod tests {
view_3.keymap_context.add_identifier("b"); view_3.keymap_context.add_identifier("b");
view_3.keymap_context.add_identifier("c"); view_3.keymap_context.add_identifier("c");
let (window_id, _view_1) = cx.add_window(Default::default(), |cx| { let window = cx.add_window(Default::default(), |cx| {
let view_2 = cx.add_view(|cx| { let view_2 = cx.add_view(|cx| {
let view_3 = cx.add_view(|cx| { let view_3 = cx.add_view(|cx| {
cx.focus_self(); cx.focus_self();
@ -5947,26 +6088,26 @@ mod tests {
} }
}); });
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("a").unwrap()) cx.dispatch_keystroke(&Keystroke::parse("a").unwrap())
}); });
assert_eq!(&*actions.borrow(), &["2 a"]); assert_eq!(&*actions.borrow(), &["2 a"]);
actions.borrow_mut().clear(); actions.borrow_mut().clear();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("b").unwrap()); cx.dispatch_keystroke(&Keystroke::parse("b").unwrap());
}); });
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]); assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
actions.borrow_mut().clear(); actions.borrow_mut().clear();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("c").unwrap()); cx.dispatch_keystroke(&Keystroke::parse("c").unwrap());
}); });
assert_eq!(&*actions.borrow(), &["3 c"]); assert_eq!(&*actions.borrow(), &["3 c"]);
actions.borrow_mut().clear(); actions.borrow_mut().clear();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("d").unwrap()); cx.dispatch_keystroke(&Keystroke::parse("d").unwrap());
}); });
assert_eq!(&*actions.borrow(), &["2 d"]); assert_eq!(&*actions.borrow(), &["2 d"]);
@ -6006,13 +6147,14 @@ mod tests {
} }
} }
let (window_id, view_1) = cx.add_window(|cx| { let window = cx.add_window(|cx| {
let view_2 = cx.add_view(|cx| { let view_2 = cx.add_view(|cx| {
cx.focus_self(); cx.focus_self();
View2 {} View2 {}
}); });
View1 { child: view_2 } View1 { child: view_2 }
}); });
let view_1 = window.root(cx);
let view_2 = view_1.read_with(cx, |view, _| view.child.clone()); let view_2 = view_1.read_with(cx, |view, _| view.child.clone());
cx.update(|cx| { cx.update(|cx| {
@ -6076,7 +6218,7 @@ mod tests {
// Check that global actions do not have a binding, even if a binding does exist in another view // Check that global actions do not have a binding, even if a binding does exist in another view
assert_eq!( assert_eq!(
&available_actions(window_id, view_1.id(), cx), &available_actions(window.window_id(), view_1.id(), cx),
&[ &[
("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
("test::GlobalAction", vec![]) ("test::GlobalAction", vec![])
@ -6085,7 +6227,7 @@ mod tests {
// Check that view 1 actions and bindings are available even when called from view 2 // Check that view 1 actions and bindings are available even when called from view 2
assert_eq!( assert_eq!(
&available_actions(window_id, view_2.id(), cx), &available_actions(window.window_id(), view_2.id(), cx),
&[ &[
("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
("test::Action2", vec![Keystroke::parse("b").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]),
@ -6138,7 +6280,8 @@ mod tests {
impl_actions!(test, [ActionWithArg]); impl_actions!(test, [ActionWithArg]);
let (window_id, view) = cx.add_window(|_| View); let window = cx.add_window(|_| View);
let view = window.root(cx);
cx.update(|cx| { cx.update(|cx| {
cx.add_global_action(|_: &ActionWithArg, _| {}); cx.add_global_action(|_: &ActionWithArg, _| {});
cx.add_bindings(vec![ cx.add_bindings(vec![
@ -6147,7 +6290,7 @@ mod tests {
]); ]);
}); });
let actions = cx.available_actions(window_id, view.id()); let actions = cx.available_actions(window.window_id(), view.id());
assert_eq!( assert_eq!(
actions[0].1.as_any().downcast_ref::<ActionWithArg>(), actions[0].1.as_any().downcast_ref::<ActionWithArg>(),
Some(&ActionWithArg { arg: false }) Some(&ActionWithArg { arg: false })
@ -6250,7 +6393,8 @@ mod tests {
} }
} }
let (_, view) = cx.add_window(|_| Counter(0)); let window = cx.add_window(|_| Counter(0));
let view = window.root(cx);
let condition1 = view.condition(cx, |view, _| view.0 == 2); let condition1 = view.condition(cx, |view, _| view.0 == 2);
let condition2 = view.condition(cx, |view, _| view.0 == 3); let condition2 = view.condition(cx, |view, _| view.0 == 3);
@ -6272,15 +6416,15 @@ mod tests {
#[crate::test(self)] #[crate::test(self)]
#[should_panic] #[should_panic]
async fn test_view_condition_timeout(cx: &mut TestAppContext) { async fn test_view_condition_timeout(cx: &mut TestAppContext) {
let (_, view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
view.condition(cx, |_, _| false).await; window.root(cx).condition(cx, |_, _| false).await;
} }
#[crate::test(self)] #[crate::test(self)]
#[should_panic(expected = "view dropped with pending condition")] #[should_panic(expected = "view dropped with pending condition")]
async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) { async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) {
let (window_id, _root_view) = cx.add_window(|_| TestView::default()); let window = cx.add_window(|_| TestView::default());
let view = cx.add_view(window_id, |_| TestView::default()); let view = window.add_view(cx, |_| TestView::default());
let condition = view.condition(cx, |_, _| false); let condition = view.condition(cx, |_, _| false);
cx.update(|_| drop(view)); cx.update(|_| drop(view));
@ -6288,7 +6432,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 {
@ -6305,22 +6449,21 @@ mod tests {
} }
} }
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0)); let window = cx.add_window(|_| View(0));
cx.update_window(window_id, |cx| { let root_view = window.root(cx);
window.update(cx, |cx| {
assert_eq!( assert_eq!(
cx.window.rendered_views[&root_view.id()].name(), cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 0") Some("render count: 0")
); );
}); });
let view = cx let view = window.update(cx, |cx| {
.update_window(window_id, |cx| {
cx.refresh_windows(); cx.refresh_windows();
cx.add_view(|_| View(0)) cx.add_view(|_| View(0))
}) });
.unwrap();
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
assert_eq!( assert_eq!(
cx.window.rendered_views[&root_view.id()].name(), cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 1") Some("render count: 1")
@ -6333,7 +6476,7 @@ mod tests {
cx.update(|cx| cx.refresh_windows()); cx.update(|cx| cx.refresh_windows());
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
assert_eq!( assert_eq!(
cx.window.rendered_views[&root_view.id()].name(), cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 2") Some("render count: 2")
@ -6349,7 +6492,7 @@ mod tests {
drop(view); drop(view);
}); });
cx.update_window(window_id, |cx| { window.update(cx, |cx| {
assert_eq!( assert_eq!(
cx.window.rendered_views[&root_view.id()].name(), cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 3") Some("render count: 3")
@ -6397,7 +6540,7 @@ mod tests {
} }
let events = Rc::new(RefCell::new(Vec::new())); let events = Rc::new(RefCell::new(Vec::new()));
let (window_1, _) = cx.add_window(|cx: &mut ViewContext<View>| { let window_1 = cx.add_window(|cx: &mut ViewContext<View>| {
cx.observe_window_activation({ cx.observe_window_activation({
let events = events.clone(); let events = events.clone();
move |this, active, _| events.borrow_mut().push((this.0, active)) move |this, active, _| events.borrow_mut().push((this.0, active))
@ -6407,7 +6550,7 @@ mod tests {
}); });
assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]); assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]);
let (window_2, _) = cx.add_window(|cx: &mut ViewContext<View>| { let window_2 = cx.add_window(|cx: &mut ViewContext<View>| {
cx.observe_window_activation({ cx.observe_window_activation({
let events = events.clone(); let events = events.clone();
move |this, active, _| events.borrow_mut().push((this.0, active)) move |this, active, _| events.borrow_mut().push((this.0, active))
@ -6420,7 +6563,7 @@ mod tests {
[("window 1", false), ("window 2", true)] [("window 1", false), ("window 2", true)]
); );
let (window_3, _) = cx.add_window(|cx: &mut ViewContext<View>| { let window_3 = cx.add_window(|cx: &mut ViewContext<View>| {
cx.observe_window_activation({ cx.observe_window_activation({
let events = events.clone(); let events = events.clone();
move |this, active, _| events.borrow_mut().push((this.0, active)) move |this, active, _| events.borrow_mut().push((this.0, active))
@ -6433,25 +6576,25 @@ mod tests {
[("window 2", false), ("window 3", true)] [("window 2", false), ("window 3", true)]
); );
cx.simulate_window_activation(Some(window_2)); cx.simulate_window_activation(Some(window_2.window_id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 3", false), ("window 2", true)] [("window 3", false), ("window 2", true)]
); );
cx.simulate_window_activation(Some(window_1)); cx.simulate_window_activation(Some(window_1.window_id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 2", false), ("window 1", true)] [("window 2", false), ("window 1", true)]
); );
cx.simulate_window_activation(Some(window_3)); cx.simulate_window_activation(Some(window_3.window_id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 1", false), ("window 3", true)] [("window 1", false), ("window 3", true)]
); );
cx.simulate_window_activation(Some(window_3)); cx.simulate_window_activation(Some(window_3.window_id()));
assert_eq!(mem::take(&mut *events.borrow_mut()), []); assert_eq!(mem::take(&mut *events.borrow_mut()), []);
} }
@ -6507,12 +6650,13 @@ mod tests {
let child_rendered = Rc::new(Cell::new(false)); let child_rendered = Rc::new(Cell::new(false));
let child_dropped = Rc::new(Cell::new(false)); let child_dropped = Rc::new(Cell::new(false));
let (_, root_view) = cx.add_window(|cx| Parent { let window = cx.add_window(|cx| Parent {
child: Some(cx.add_view(|_| Child { child: Some(cx.add_view(|_| Child {
rendered: child_rendered.clone(), rendered: child_rendered.clone(),
dropped: child_dropped.clone(), dropped: child_dropped.clone(),
})), })),
}); });
let root_view = window.root(cx);
assert!(child_rendered.take()); assert!(child_rendered.take());
assert!(!child_dropped.take()); assert!(!child_dropped.take());

View file

@ -6,7 +6,7 @@ use crate::{
platform::{Event, InputHandler, KeyDownEvent, Platform}, platform::{Event, InputHandler, KeyDownEvent, Platform},
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle, Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
WindowContext, WindowContext, WindowHandle,
}; };
use collections::BTreeMap; use collections::BTreeMap;
use futures::Future; use futures::Future;
@ -60,7 +60,7 @@ impl TestAppContext {
RefCounts::new(leak_detector), RefCounts::new(leak_detector),
(), (),
); );
cx.next_entity_id = first_entity_id; cx.next_id = first_entity_id;
let cx = TestAppContext { let cx = TestAppContext {
cx: Rc::new(RefCell::new(cx)), cx: Rc::new(RefCell::new(cx)),
foreground_platform, foreground_platform,
@ -148,17 +148,18 @@ impl TestAppContext {
self.cx.borrow_mut().add_model(build_model) self.cx.borrow_mut().add_model(build_model)
} }
pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>) pub fn add_window<T, F>(&mut self, build_root_view: F) -> WindowHandle<T>
where where
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> T, F: FnOnce(&mut ViewContext<T>) -> T,
{ {
let (window_id, view) = self let window = self
.cx .cx
.borrow_mut() .borrow_mut()
.add_window(Default::default(), build_root_view); .add_window(Default::default(), build_root_view);
self.simulate_window_activation(Some(window_id)); self.simulate_window_activation(Some(window.window_id()));
(window_id, view)
WindowHandle::new(window.window_id())
} }
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T> pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
@ -405,14 +406,20 @@ impl BorrowAppContext for TestAppContext {
} }
impl BorrowWindowContext for TestAppContext { impl BorrowWindowContext for TestAppContext {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { type Result<T> = 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

@ -15,7 +15,7 @@ use crate::{
util::post_inc, util::post_inc,
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect, Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription, Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription,
View, ViewContext, ViewHandle, WindowInvalidation, View, ViewContext, ViewHandle, WindowHandle, WindowInvalidation,
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
@ -142,7 +142,9 @@ impl BorrowAppContext for WindowContext<'_> {
} }
impl BorrowWindowContext for WindowContext<'_> { impl BorrowWindowContext for WindowContext<'_> {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { type Result<T> = 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 {
@ -150,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 {
@ -1151,15 +1157,15 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.prompt(level, msg, answers) self.window.platform_window.prompt(level, msg, answers)
} }
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V> pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
where where
V: View, V: View,
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
let root_view = self.add_view(|cx| build_root_view(cx)); let root_view = self.add_view(|cx| build_root_view(cx));
self.window.root_view = Some(root_view.clone().into_any());
self.window.focused_view_id = Some(root_view.id()); self.window.focused_view_id = Some(root_view.id());
root_view self.window.root_view = Some(root_view.into_any());
WindowHandle::new(self.window_id)
} }
pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T> pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T>
@ -1176,7 +1182,7 @@ impl<'a> WindowContext<'a> {
F: FnOnce(&mut ViewContext<T>) -> Option<T>, F: FnOnce(&mut ViewContext<T>) -> Option<T>,
{ {
let window_id = self.window_id; let window_id = self.window_id;
let view_id = post_inc(&mut self.next_entity_id); let view_id = post_inc(&mut self.next_id);
let mut cx = ViewContext::mutable(self, view_id); let mut cx = ViewContext::mutable(self, view_id);
let handle = if let Some(view) = build_view(&mut cx) { let handle = if let Some(view) = build_view(&mut cx) {
let mut keymap_context = KeymapContext::default(); let mut keymap_context = KeymapContext::default();

View file

@ -61,7 +61,9 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
.receive_notification::<lsp::notification::DidOpenTextDocument>() .receive_notification::<lsp::notification::DidOpenTextDocument>()
.await; .await;
let (_, log_view) = cx.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)); let log_view = cx
.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), 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

@ -1780,7 +1780,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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),
@ -1868,7 +1870,9 @@ mod tests {
.await; .await;
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 (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "root1", cx); select_path(&panel, "root1", cx);
@ -2219,7 +2223,9 @@ mod tests {
.await; .await;
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 (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "root1", cx); select_path(&panel, "root1", cx);
@ -2319,7 +2325,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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| {
@ -2392,7 +2400,9 @@ mod tests {
.await; .await;
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
toggle_expand_dir(&panel, "src/test", cx); toggle_expand_dir(&panel, "src/test", cx);
@ -2481,7 +2491,9 @@ mod tests {
.await; .await;
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "src/", cx); select_path(&panel, "src/", cx);
@ -2627,7 +2639,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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));
@ -2714,7 +2728,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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

@ -326,7 +326,9 @@ mod tests {
}, },
); );
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
// Create the project symbols view. // Create the project symbols view.
let symbols = cx.add_view(window_id, |cx| { let symbols = cx.add_view(window_id, |cx| {

View file

@ -849,11 +849,13 @@ mod tests {
cx, cx,
) )
}); });
let (window_id, _root_view) = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); let editor = cx.add_view(window.window_id(), |cx| {
Editor::for_buffer(buffer.clone(), None, cx)
});
let search_bar = cx.add_view(window_id, |cx| { let search_bar = cx.add_view(window.window_id(), |cx| {
let mut search_bar = BufferSearchBar::new(cx); let mut search_bar = BufferSearchBar::new(cx);
search_bar.set_active_pane_item(Some(&editor), cx); search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(cx); search_bar.show(cx);
@ -1229,7 +1231,8 @@ mod tests {
"Should pick a query with multiple results" "Should pick a query with multiple results"
); );
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
let (window_id, _root_view) = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let window_id = window.window_id();
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx));
@ -1416,11 +1419,13 @@ mod tests {
"# "#
.unindent(); .unindent();
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
let (window_id, _root_view) = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); let editor = cx.add_view(window.window_id(), |cx| {
Editor::for_buffer(buffer.clone(), None, cx)
});
let search_bar = cx.add_view(window_id, |cx| { let search_bar = cx.add_view(window.window_id(), |cx| {
let mut search_bar = BufferSearchBar::new(cx); let mut search_bar = BufferSearchBar::new(cx);
search_bar.set_active_pane_item(Some(&editor), cx); search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(cx); search_bar.show(cx);

View file

@ -1447,7 +1447,9 @@ pub mod tests {
.await; .await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
let (_, search_view) = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx)); let search_view = cx
.add_window(|cx| ProjectSearchView::new(search.clone(), cx))
.root(cx);
search_view.update(cx, |search_view, cx| { search_view.update(cx, |search_view, cx| {
search_view search_view
@ -1564,7 +1566,9 @@ pub mod tests {
) )
.await; .await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let active_item = cx.read(|cx| { let active_item = cx.read(|cx| {
workspace workspace
@ -1748,7 +1752,9 @@ pub mod tests {
let worktree_id = project.read_with(cx, |project, cx| { let worktree_id = project.read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
}); });
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project, cx))
.root(cx);
let active_item = cx.read(|cx| { let active_item = cx.read(|cx| {
workspace workspace
@ -1866,7 +1872,9 @@ pub mod tests {
) )
.await; .await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
}); });

View file

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

View file

@ -1972,7 +1972,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
@ -1987,7 +1988,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// 1. Add with a destination index // 1. Add with a destination index
@ -2065,7 +2067,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// 1. Add with a destination index // 1. Add with a destination index
@ -2141,7 +2144,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// singleton view // singleton view
@ -2209,7 +2213,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
add_labeled_item(&pane, "A", false, cx); add_labeled_item(&pane, "A", false, cx);
@ -2256,7 +2261,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
@ -2276,7 +2282,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
add_labeled_item(&pane, "A", true, cx); add_labeled_item(&pane, "A", true, cx);
@ -2299,7 +2306,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
@ -2319,7 +2327,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
@ -2339,7 +2348,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
add_labeled_item(&pane, "A", false, cx); add_labeled_item(&pane, "A", false, cx);

View file

@ -793,20 +793,14 @@ impl Workspace {
DB.next_id().await.unwrap_or(0) DB.next_id().await.unwrap_or(0)
}; };
let workspace = 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::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
workspace_id,
project_handle.clone(),
app_state.clone(),
cx,
)
}) })
}) })
}) });
.unwrap_or_else(|| { let window = window.unwrap_or_else(|| {
let window_bounds_override = window_bounds_env_override(&cx); let window_bounds_override = window_bounds_env_override(&cx);
let (bounds, display) = if let Some(bounds) = window_bounds_override { let (bounds, display) = if let Some(bounds) = window_bounds_override {
(Some(bounds), None) (Some(bounds), None)
@ -844,17 +838,15 @@ impl Workspace {
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,
)
}, },
) )
.1
}); });
// We haven't yielded the main thread since obtaining the window handle,
// so the window exists.
let workspace = window.root(&cx).unwrap();
(app_state.initialize_workspace)( (app_state.initialize_workspace)(
workspace.downgrade(), workspace.downgrade(),
serialized_workspace.is_some(), serialized_workspace.is_some(),
@ -864,7 +856,7 @@ impl Workspace {
.await .await
.log_err(); .log_err();
cx.update_window(workspace.window_id(), |cx| cx.activate_window()); window.update(&mut cx, |cx| cx.activate_window());
let workspace = workspace.downgrade(); let workspace = workspace.downgrade();
notify_if_database_failed(&workspace, &mut cx); notify_if_database_failed(&workspace, &mut cx);
@ -3977,7 +3969,7 @@ pub fn join_remote_project(
.await?; .await?;
let window_bounds_override = window_bounds_env_override(&cx); let window_bounds_override = window_bounds_env_override(&cx);
let (_, workspace) = cx.add_window( let window = cx.add_window(
(app_state.build_window_options)( (app_state.build_window_options)(
window_bounds_override, window_bounds_override,
None, None,
@ -3985,6 +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).unwrap();
(app_state.initialize_workspace)( (app_state.initialize_workspace)(
workspace.downgrade(), workspace.downgrade(),
false, false,
@ -4113,10 +4106,11 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
// Adding an item with no ambiguity renders the tab without detail. // Adding an item with no ambiguity renders the tab without detail.
let item1 = cx.add_view(window_id, |_| { let item1 = window.add_view(cx, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
item item
@ -4128,7 +4122,7 @@ mod tests {
// Adding an item that creates ambiguity increases the level of detail on // Adding an item that creates ambiguity increases the level of detail on
// both tabs. // both tabs.
let item2 = cx.add_view(window_id, |_| { let item2 = window.add_view(cx, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
item item
@ -4142,7 +4136,7 @@ mod tests {
// Adding an item that creates ambiguity increases the level of detail only // Adding an item that creates ambiguity increases the level of detail only
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so // on the ambiguous tabs. In this case, the ambiguity can't be resolved so
// we stop at the highest detail available. // we stop at the highest detail available.
let item3 = cx.add_view(window_id, |_| { let item3 = window.add_view(cx, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
item item
@ -4177,16 +4171,17 @@ mod tests {
.await; .await;
let project = Project::test(fs, ["root1".as_ref()], cx).await; let project = Project::test(fs, ["root1".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.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 worktree_id = project.read_with(cx, |project, cx| { let worktree_id = project.read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
}); });
let item1 = cx.add_view(window_id, |cx| { let item1 = window.add_view(cx, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]) TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
}); });
let item2 = cx.add_view(window_id, |cx| { let item2 = window.add_view(cx, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)]) TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
}); });
@ -4201,14 +4196,14 @@ mod tests {
); );
}); });
assert_eq!( assert_eq!(
cx.current_window_title(window_id).as_deref(), cx.current_window_title(window.window_id()).as_deref(),
Some("one.txt — root1") Some("one.txt — root1")
); );
// Add a second item to a non-empty pane // Add a second item to a non-empty pane
workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
assert_eq!( assert_eq!(
cx.current_window_title(window_id).as_deref(), cx.current_window_title(window.window_id()).as_deref(),
Some("two.txt — root1") Some("two.txt — root1")
); );
project.read_with(cx, |project, cx| { project.read_with(cx, |project, cx| {
@ -4227,7 +4222,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
cx.current_window_title(window_id).as_deref(), cx.current_window_title(window.window_id()).as_deref(),
Some("one.txt — root1") Some("one.txt — root1")
); );
project.read_with(cx, |project, cx| { project.read_with(cx, |project, cx| {
@ -4247,14 +4242,14 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
cx.current_window_title(window_id).as_deref(), cx.current_window_title(window.window_id()).as_deref(),
Some("one.txt — root1, root2") Some("one.txt — root1, root2")
); );
// Remove a project folder // Remove a project folder
project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
assert_eq!( assert_eq!(
cx.current_window_title(window_id).as_deref(), cx.current_window_title(window.window_id()).as_deref(),
Some("one.txt — root2") Some("one.txt — root2")
); );
} }
@ -4267,18 +4262,19 @@ mod tests {
fs.insert_tree("/root", json!({ "one": "" })).await; fs.insert_tree("/root", json!({ "one": "" })).await;
let project = Project::test(fs, ["root".as_ref()], cx).await; let project = Project::test(fs, ["root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
// When there are no dirty items, there's nothing to do. // When there are no dirty items, there's nothing to do.
let item1 = cx.add_view(window_id, |_| TestItem::new()); let item1 = window.add_view(cx, |_| TestItem::new());
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
assert!(task.await.unwrap()); assert!(task.await.unwrap());
// When there are dirty untitled items, prompt to save each one. If the user // When there are dirty untitled items, prompt to save each one. If the user
// cancels any prompt, then abort. // cancels any prompt, then abort.
let item2 = cx.add_view(window_id, |_| TestItem::new().with_dirty(true)); let item2 = window.add_view(cx, |_| TestItem::new().with_dirty(true));
let item3 = cx.add_view(window_id, |cx| { let item3 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
@ -4289,9 +4285,9 @@ mod tests {
}); });
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.simulate_prompt_answer(window_id, 2 /* cancel */); cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
assert!(!cx.has_pending_prompt(window_id)); assert!(!cx.has_pending_prompt(window.window_id()));
assert!(!task.await.unwrap()); assert!(!task.await.unwrap());
} }
@ -4302,26 +4298,27 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let item1 = cx.add_view(window_id, |cx| { let item1 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
}); });
let item2 = cx.add_view(window_id, |cx| { let item2 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_conflict(true) .with_conflict(true)
.with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]) .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
}); });
let item3 = cx.add_view(window_id, |cx| { let item3 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_conflict(true) .with_conflict(true)
.with_project_items(&[TestProjectItem::new(3, "3.txt", cx)]) .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
}); });
let item4 = cx.add_view(window_id, |cx| { let item4 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_project_items(&[TestProjectItem::new_untitled(cx)]) .with_project_items(&[TestProjectItem::new_untitled(cx)])
@ -4349,10 +4346,10 @@ mod tests {
assert_eq!(pane.items_len(), 4); assert_eq!(pane.items_len(), 4);
assert_eq!(pane.active_item().unwrap().id(), item1.id()); assert_eq!(pane.active_item().unwrap().id(), item1.id());
}); });
assert!(cx.has_pending_prompt(window_id)); assert!(cx.has_pending_prompt(window.window_id()));
// Confirm saving item 1. // Confirm saving item 1.
cx.simulate_prompt_answer(window_id, 0); cx.simulate_prompt_answer(window.window_id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// Item 1 is saved. There's a prompt to save item 3. // Item 1 is saved. There's a prompt to save item 3.
@ -4363,10 +4360,10 @@ mod tests {
assert_eq!(pane.items_len(), 3); assert_eq!(pane.items_len(), 3);
assert_eq!(pane.active_item().unwrap().id(), item3.id()); assert_eq!(pane.active_item().unwrap().id(), item3.id());
}); });
assert!(cx.has_pending_prompt(window_id)); assert!(cx.has_pending_prompt(window.window_id()));
// Cancel saving item 3. // Cancel saving item 3.
cx.simulate_prompt_answer(window_id, 1); cx.simulate_prompt_answer(window.window_id(), 1);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// Item 3 is reloaded. There's a prompt to save item 4. // Item 3 is reloaded. There's a prompt to save item 4.
@ -4377,10 +4374,10 @@ mod tests {
assert_eq!(pane.items_len(), 2); assert_eq!(pane.items_len(), 2);
assert_eq!(pane.active_item().unwrap().id(), item4.id()); assert_eq!(pane.active_item().unwrap().id(), item4.id());
}); });
assert!(cx.has_pending_prompt(window_id)); assert!(cx.has_pending_prompt(window.window_id()));
// Confirm saving item 4. // Confirm saving item 4.
cx.simulate_prompt_answer(window_id, 0); cx.simulate_prompt_answer(window.window_id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// There's a prompt for a path for item 4. // There's a prompt for a path for item 4.
@ -4404,13 +4401,14 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
// Create several workspace items with single project entries, and two // Create several workspace items with single project entries, and two
// workspace items with multiple project entries. // workspace items with multiple project entries.
let single_entry_items = (0..=4) let single_entry_items = (0..=4)
.map(|project_entry_id| { .map(|project_entry_id| {
cx.add_view(window_id, |cx| { window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_project_items(&[TestProjectItem::new( .with_project_items(&[TestProjectItem::new(
@ -4421,7 +4419,7 @@ mod tests {
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let item_2_3 = cx.add_view(window_id, |cx| { let item_2_3 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_singleton(false) .with_singleton(false)
@ -4430,7 +4428,7 @@ mod tests {
single_entry_items[3].read(cx).project_items[0].clone(), single_entry_items[3].read(cx).project_items[0].clone(),
]) ])
}); });
let item_3_4 = cx.add_view(window_id, |cx| { let item_3_4 = window.add_view(cx, |cx| {
TestItem::new() TestItem::new()
.with_dirty(true) .with_dirty(true)
.with_singleton(false) .with_singleton(false)
@ -4482,7 +4480,7 @@ mod tests {
&[ProjectEntryId::from_proto(0)] &[ProjectEntryId::from_proto(0)]
); );
}); });
cx.simulate_prompt_answer(window_id, 0); cx.simulate_prompt_answer(window.window_id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
left_pane.read_with(cx, |pane, cx| { left_pane.read_with(cx, |pane, cx| {
@ -4491,7 +4489,7 @@ mod tests {
&[ProjectEntryId::from_proto(2)] &[ProjectEntryId::from_proto(2)]
); );
}); });
cx.simulate_prompt_answer(window_id, 0); cx.simulate_prompt_answer(window.window_id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
close.await.unwrap(); close.await.unwrap();
@ -4507,10 +4505,11 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.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 item = cx.add_view(window_id, |cx| { let item = window.add_view(cx, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
}); });
let item_id = item.id(); let item_id = item.id();
@ -4550,7 +4549,7 @@ mod tests {
item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
// Deactivating the window still saves the file. // Deactivating the window still saves the file.
cx.simulate_window_activation(Some(window_id)); cx.simulate_window_activation(Some(window.window_id()));
item.update(cx, |item, cx| { item.update(cx, |item, cx| {
cx.focus_self(); cx.focus_self();
item.is_dirty = true; item.is_dirty = true;
@ -4592,7 +4591,7 @@ mod tests {
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id))
.await .await
.unwrap(); .unwrap();
assert!(!cx.has_pending_prompt(window_id)); assert!(!cx.has_pending_prompt(window.window_id()));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
// Add the item again, ensuring autosave is prevented if the underlying file has been deleted. // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
@ -4613,7 +4612,7 @@ mod tests {
let _close_items = let _close_items =
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id));
deterministic.run_until_parked(); deterministic.run_until_parked();
assert!(cx.has_pending_prompt(window_id)); assert!(cx.has_pending_prompt(window.window_id()));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
} }
@ -4624,9 +4623,10 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let item = cx.add_view(window_id, |cx| { let item = window.add_view(cx, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
}); });
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
@ -4677,7 +4677,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let panel = workspace.update(cx, |workspace, cx| { let panel = workspace.update(cx, |workspace, cx| {
let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right));
@ -4824,7 +4825,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| { let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| {
// Add panel_1 on the left, panel_2 on the right. // Add panel_1 on the left, panel_2 on the right.
@ -4979,7 +4981,7 @@ mod tests {
// If focus is transferred to another view that's not a panel or another pane, we still show // If focus is transferred to another view that's not a panel or another pane, we still show
// the panel as zoomed. // the panel as zoomed.
let focus_receiver = cx.add_view(window_id, |_| EmptyView); let focus_receiver = window.add_view(cx, |_| EmptyView);
focus_receiver.update(cx, |_, cx| cx.focus_self()); focus_receiver.update(cx, |_, cx| cx.focus_self());
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));

View file

@ -982,7 +982,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project, 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();
@ -1294,7 +1296,9 @@ mod tests {
.await; .await;
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 (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
// Open a file within an existing worktree. // Open a file within an existing worktree.
workspace workspace
@ -1335,7 +1339,9 @@ 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;
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
// Create a new untitled buffer // Create a new untitled buffer
@ -1428,7 +1434,9 @@ mod tests {
let project = Project::test(app_state.fs.clone(), [], cx).await; let project = Project::test(app_state.fs.clone(), [], cx).await;
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
// Create a new untitled buffer // Create a new untitled buffer
cx.dispatch_action(window_id, NewFile); cx.dispatch_action(window_id, NewFile);
@ -1479,7 +1487,9 @@ mod tests {
.await; .await;
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 (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
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();
@ -1553,7 +1563,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project.clone(), 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));
@ -1830,7 +1842,9 @@ mod tests {
.await; .await;
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.add_window(|cx| Workspace::test_new(project, cx)); let workspace = cx
.add_window(|cx| Workspace::test_new(project, 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));
@ -2072,7 +2086,8 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let (window_id, _view) = cx.add_window(|_| TestView); let window = cx.add_window(|_| TestView);
let window_id = window.window_id();
// Test loading the keymap base at all // Test loading the keymap base at all
assert_key_bindings_for( assert_key_bindings_for(
@ -2242,7 +2257,8 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let (window_id, _view) = cx.add_window(|_| TestView); let window = cx.add_window(|_| TestView);
let window_id = window.window_id();
// Test loading the keymap base at all // Test loading the keymap base at all
assert_key_bindings_for( assert_key_bindings_for(