checkpoint

Mikayla created

Change summary

crates/client2/src/test.rs                     |   2 
crates/collab2/src/db/tests/db_tests.rs        |   2 
crates/collab2/src/tests/editor_tests.rs       |   4 
crates/collab2/src/tests/integration_tests.rs  | 752 --------------------
crates/collab2/src/tests/test_server.rs        |   4 
crates/copilot2/src/copilot2.rs                |  45 
crates/editor2/src/editor_tests.rs             | 517 ++++++-------
crates/editor2/src/test.rs                     |   2 
crates/editor2/src/test/editor_test_context.rs |   2 
crates/gpui2/src/app.rs                        |  17 
crates/gpui2/src/app/async_context.rs          |  22 
crates/gpui2/src/app/model_context.rs          |  13 
crates/gpui2/src/app/test_context.rs           |  15 
crates/gpui2/src/color.rs                      |  33 
crates/gpui2/src/gpui2.rs                      |  10 
crates/gpui2/src/window.rs                     |  78 +
crates/language2/src/language2.rs              |   4 
crates/prettier2/src/prettier2.rs              |  10 
crates/project2/src/project2.rs                |   2 
crates/project2/src/project_tests.rs           |  68 
crates/rpc2/src/peer.rs                        |  20 
crates/workspace2/src/workspace2.rs            |  42 
22 files changed, 481 insertions(+), 1,183 deletions(-)

Detailed changes

crates/client2/src/test.rs πŸ”—

@@ -36,7 +36,7 @@ impl FakeServer {
             peer: Peer::new(0),
             state: Default::default(),
             user_id: client_user_id,
-            executor: cx.executor().clone(),
+            executor: cx.executor(),
         };
 
         client

crates/collab2/src/db/tests/db_tests.rs πŸ”—

@@ -510,7 +510,7 @@ fn test_fuzzy_like_string() {
 
 #[gpui::test]
 async fn test_fuzzy_search_users(cx: &mut TestAppContext) {
-    let test_db = TestDb::postgres(cx.executor().clone());
+    let test_db = TestDb::postgres(cx.executor());
     let db = test_db.db();
     for (i, github_login) in [
         "California",

crates/collab2/src/tests/editor_tests.rs πŸ”—

@@ -48,9 +48,9 @@ async fn test_host_disconnect(
 
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 
-    let window_b =
+    let workspace_b =
         cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
-    let workspace_b = window_b.root(cx_b);
+
     let editor_b = workspace_b
         .update(cx_b, |workspace, cx| {
             workspace.open_path((worktree_id, "b.txt"), None, true, cx)

crates/collab2/src/tests/integration_tests.rs πŸ”—

@@ -5717,755 +5717,3 @@ async fn test_join_call_after_screen_was_shared(
         );
     });
 }
-
-#[gpui::test(iterations = 10)]
-async fn test_on_input_format_from_host_to_guest(
-    executor: BackgroundExecutor,
-    cx_a: &mut TestAppContext,
-    cx_b: &mut TestAppContext,
-) {
-    let mut server = TestServer::start(&executor).await;
-    let client_a = server.create_client(cx_a, "user_a").await;
-    let client_b = server.create_client(cx_b, "user_b").await;
-    server
-        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-        .await;
-    let active_call_a = cx_a.read(ActiveCall::global);
-
-    // Set up a fake language server.
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_language_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
-                    first_trigger_character: ":".to_string(),
-                    more_trigger_character: Some(vec![">".to_string()]),
-                }),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-    client_a.language_registry().add(Arc::new(language));
-
-    client_a
-        .fs()
-        .insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a }",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_id = active_call_a
-        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-        .await
-        .unwrap();
-    let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-    // Open a file in an editor as the host.
-    let buffer_a = project_a
-        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-        .await
-        .unwrap();
-    let window_a = cx_a.add_window(|_| EmptyView);
-    let editor_a = window_a.add_view(cx_a, |cx| {
-        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
-    });
-
-    let fake_language_server = fake_language_servers.next().await.unwrap();
-    executor.run_until_parked();
-
-    // Receive an OnTypeFormatting request as the host's language server.
-    // Return some formattings from the host's language server.
-    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
-        |params, _| async move {
-            assert_eq!(
-                params.text_document_position.text_document.uri,
-                lsp::Url::from_file_path("/a/main.rs").unwrap(),
-            );
-            assert_eq!(
-                params.text_document_position.position,
-                lsp::Position::new(0, 14),
-            );
-
-            Ok(Some(vec![lsp::TextEdit {
-                new_text: "~<".to_string(),
-                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
-            }]))
-        },
-    );
-
-    // Open the buffer on the guest and see that the formattings worked
-    let buffer_b = project_b
-        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-        .await
-        .unwrap();
-
-    // Type a on type formatting trigger character as the guest.
-    editor_a.update(cx_a, |editor, cx| {
-        cx.focus(&editor_a);
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input(">", cx);
-    });
-
-    executor.run_until_parked();
-
-    buffer_b.read_with(cx_b, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a>~< }")
-    });
-
-    // Undo should remove LSP edits first
-    editor_a.update(cx_a, |editor, cx| {
-        assert_eq!(editor.text(cx), "fn main() { a>~< }");
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "fn main() { a> }");
-    });
-    executor.run_until_parked();
-
-    buffer_b.read_with(cx_b, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a> }")
-    });
-
-    editor_a.update(cx_a, |editor, cx| {
-        assert_eq!(editor.text(cx), "fn main() { a> }");
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "fn main() { a }");
-    });
-    executor.run_until_parked();
-
-    buffer_b.read_with(cx_b, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a }")
-    });
-}
-
-#[gpui::test(iterations = 10)]
-async fn test_on_input_format_from_guest_to_host(
-    executor: BackgroundExecutor,
-    cx_a: &mut TestAppContext,
-    cx_b: &mut TestAppContext,
-) {
-    let mut server = TestServer::start(&executor).await;
-    let client_a = server.create_client(cx_a, "user_a").await;
-    let client_b = server.create_client(cx_b, "user_b").await;
-    server
-        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-        .await;
-    let active_call_a = cx_a.read(ActiveCall::global);
-
-    // Set up a fake language server.
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_language_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
-                    first_trigger_character: ":".to_string(),
-                    more_trigger_character: Some(vec![">".to_string()]),
-                }),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-    client_a.language_registry().add(Arc::new(language));
-
-    client_a
-        .fs()
-        .insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a }",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_id = active_call_a
-        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-        .await
-        .unwrap();
-    let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-    // Open a file in an editor as the guest.
-    let buffer_b = project_b
-        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-        .await
-        .unwrap();
-    let window_b = cx_b.add_window(|_| EmptyView);
-    let editor_b = window_b.add_view(cx_b, |cx| {
-        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
-    });
-
-    let fake_language_server = fake_language_servers.next().await.unwrap();
-    executor.run_until_parked();
-    // Type a on type formatting trigger character as the guest.
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input(":", cx);
-        cx.focus(&editor_b);
-    });
-
-    // Receive an OnTypeFormatting request as the host's language server.
-    // Return some formattings from the host's language server.
-    cx_a.foreground().start_waiting();
-    fake_language_server
-        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
-            assert_eq!(
-                params.text_document_position.text_document.uri,
-                lsp::Url::from_file_path("/a/main.rs").unwrap(),
-            );
-            assert_eq!(
-                params.text_document_position.position,
-                lsp::Position::new(0, 14),
-            );
-
-            Ok(Some(vec![lsp::TextEdit {
-                new_text: "~:".to_string(),
-                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
-            }]))
-        })
-        .next()
-        .await
-        .unwrap();
-    cx_a.foreground().finish_waiting();
-
-    // Open the buffer on the host and see that the formattings worked
-    let buffer_a = project_a
-        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-        .await
-        .unwrap();
-    executor.run_until_parked();
-
-    buffer_a.read_with(cx_a, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a:~: }")
-    });
-
-    // Undo should remove LSP edits first
-    editor_b.update(cx_b, |editor, cx| {
-        assert_eq!(editor.text(cx), "fn main() { a:~: }");
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "fn main() { a: }");
-    });
-    executor.run_until_parked();
-
-    buffer_a.read_with(cx_a, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a: }")
-    });
-
-    editor_b.update(cx_b, |editor, cx| {
-        assert_eq!(editor.text(cx), "fn main() { a: }");
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "fn main() { a }");
-    });
-    executor.run_until_parked();
-
-    buffer_a.read_with(cx_a, |buffer, _| {
-        assert_eq!(buffer.text(), "fn main() { a }")
-    });
-}
-
-#[gpui::test(iterations = 10)]
-async fn test_mutual_editor_inlay_hint_cache_update(
-    executor: BackgroundExecutor,
-    cx_a: &mut TestAppContext,
-    cx_b: &mut TestAppContext,
-) {
-    let mut server = TestServer::start(&executor).await;
-    let client_a = server.create_client(cx_a, "user_a").await;
-    let client_b = server.create_client(cx_b, "user_b").await;
-    server
-        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-        .await;
-    let active_call_a = cx_a.read(ActiveCall::global);
-    let active_call_b = cx_b.read(ActiveCall::global);
-
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
-
-    cx_a.update(|cx| {
-        cx.update_global(|store: &mut SettingsStore, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: true,
-                    show_type_hints: true,
-                    show_parameter_hints: false,
-                    show_other_hints: true,
-                })
-            });
-        });
-    });
-    cx_b.update(|cx| {
-        cx.update_global(|store: &mut SettingsStore, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: true,
-                    show_type_hints: true,
-                    show_parameter_hints: false,
-                    show_other_hints: true,
-                })
-            });
-        });
-    });
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_language_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-    let language = Arc::new(language);
-    client_a.language_registry().add(Arc::clone(&language));
-    client_b.language_registry().add(language);
-
-    // Client A opens a project.
-    client_a
-        .fs()
-        .insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    active_call_a
-        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
-        .await
-        .unwrap();
-    let project_id = active_call_a
-        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-        .await
-        .unwrap();
-
-    // Client B joins the project
-    let project_b = client_b.build_remote_project(project_id, cx_b).await;
-    active_call_b
-        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
-        .await
-        .unwrap();
-
-    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
-    cx_a.foreground().start_waiting();
-
-    // The host opens a rust file.
-    let _buffer_a = project_a
-        .update(cx_a, |project, cx| {
-            project.open_local_buffer("/a/main.rs", cx)
-        })
-        .await
-        .unwrap();
-    let fake_language_server = fake_language_servers.next().await.unwrap();
-    let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    // Set up the language server to return an additional inlay hint on each request.
-    let edits_made = Arc::new(AtomicUsize::new(0));
-    let closure_edits_made = Arc::clone(&edits_made);
-    fake_language_server
-        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-            let task_edits_made = Arc::clone(&closure_edits_made);
-            async move {
-                assert_eq!(
-                    params.text_document.uri,
-                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                );
-                let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
-                Ok(Some(vec![lsp::InlayHint {
-                    position: lsp::Position::new(0, edits_made as u32),
-                    label: lsp::InlayHintLabel::String(edits_made.to_string()),
-                    kind: None,
-                    text_edits: None,
-                    tooltip: None,
-                    padding_left: None,
-                    padding_right: None,
-                    data: None,
-                }]))
-            }
-        })
-        .next()
-        .await
-        .unwrap();
-
-    executor.run_until_parked();
-
-    let initial_edit = edits_made.load(atomic::Ordering::Acquire);
-    editor_a.update(cx_a, |editor, _| {
-        assert_eq!(
-            vec![initial_edit.to_string()],
-            extract_hint_labels(editor),
-            "Host should get its first hints when opens an editor"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            1,
-            "Host editor update the cache version after every cache/view change",
-        );
-    });
-    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
-    let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    executor.run_until_parked();
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec![initial_edit.to_string()],
-            extract_hint_labels(editor),
-            "Client should get its first hints when opens an editor"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            1,
-            "Guest editor update the cache version after every cache/view change"
-        );
-    });
-
-    let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-    editor_b.update(cx_b, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
-        editor.handle_input(":", cx);
-        cx.focus(&editor_b);
-    });
-
-    executor.run_until_parked();
-    editor_a.update(cx_a, |editor, _| {
-        assert_eq!(
-            vec![after_client_edit.to_string()],
-            extract_hint_labels(editor),
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(inlay_cache.version(), 2);
-    });
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec![after_client_edit.to_string()],
-            extract_hint_labels(editor),
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(inlay_cache.version(), 2);
-    });
-
-    let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-    editor_a.update(cx_a, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-        editor.handle_input("a change to increment both buffers' versions", cx);
-        cx.focus(&editor_a);
-    });
-
-    executor.run_until_parked();
-    editor_a.update(cx_a, |editor, _| {
-        assert_eq!(
-            vec![after_host_edit.to_string()],
-            extract_hint_labels(editor),
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(inlay_cache.version(), 3);
-    });
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec![after_host_edit.to_string()],
-            extract_hint_labels(editor),
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(inlay_cache.version(), 3);
-    });
-
-    let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-    fake_language_server
-        .request::<lsp::request::InlayHintRefreshRequest>(())
-        .await
-        .expect("inlay refresh request failed");
-
-    executor.run_until_parked();
-    editor_a.update(cx_a, |editor, _| {
-        assert_eq!(
-            vec![after_special_edit_for_refresh.to_string()],
-            extract_hint_labels(editor),
-            "Host should react to /refresh LSP request"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            4,
-            "Host should accepted all edits and bump its cache version every time"
-        );
-    });
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec![after_special_edit_for_refresh.to_string()],
-            extract_hint_labels(editor),
-            "Guest should get a /refresh LSP request propagated by host"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            4,
-            "Guest should accepted all edits and bump its cache version every time"
-        );
-    });
-}
-
-#[gpui::test(iterations = 10)]
-async fn test_inlay_hint_refresh_is_forwarded(
-    executor: BackgroundExecutor,
-    cx_a: &mut TestAppContext,
-    cx_b: &mut TestAppContext,
-) {
-    let mut server = TestServer::start(&executor).await;
-    let client_a = server.create_client(cx_a, "user_a").await;
-    let client_b = server.create_client(cx_b, "user_b").await;
-    server
-        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-        .await;
-    let active_call_a = cx_a.read(ActiveCall::global);
-    let active_call_b = cx_b.read(ActiveCall::global);
-
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
-
-    cx_a.update(|cx| {
-        cx.update_global(|store: &mut SettingsStore, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: false,
-                    show_type_hints: false,
-                    show_parameter_hints: false,
-                    show_other_hints: false,
-                })
-            });
-        });
-    });
-    cx_b.update(|cx| {
-        cx.update_global(|store: &mut SettingsStore, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: true,
-                    show_type_hints: true,
-                    show_parameter_hints: true,
-                    show_other_hints: true,
-                })
-            });
-        });
-    });
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_language_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-    let language = Arc::new(language);
-    client_a.language_registry().add(Arc::clone(&language));
-    client_b.language_registry().add(language);
-
-    client_a
-        .fs()
-        .insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    active_call_a
-        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
-        .await
-        .unwrap();
-    let project_id = active_call_a
-        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-        .await
-        .unwrap();
-
-    let project_b = client_b.build_remote_project(project_id, cx_b).await;
-    active_call_b
-        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
-        .await
-        .unwrap();
-
-    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
-    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
-    cx_a.foreground().start_waiting();
-    cx_b.foreground().start_waiting();
-
-    let editor_a = workspace_a
-        .update(cx_a, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    let editor_b = workspace_b
-        .update(cx_b, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    let other_hints = Arc::new(AtomicBool::new(false));
-    let fake_language_server = fake_language_servers.next().await.unwrap();
-    let closure_other_hints = Arc::clone(&other_hints);
-    fake_language_server
-        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-            let task_other_hints = Arc::clone(&closure_other_hints);
-            async move {
-                assert_eq!(
-                    params.text_document.uri,
-                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                );
-                let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
-                let character = if other_hints { 0 } else { 2 };
-                let label = if other_hints {
-                    "other hint"
-                } else {
-                    "initial hint"
-                };
-                Ok(Some(vec![lsp::InlayHint {
-                    position: lsp::Position::new(0, character),
-                    label: lsp::InlayHintLabel::String(label.to_string()),
-                    kind: None,
-                    text_edits: None,
-                    tooltip: None,
-                    padding_left: None,
-                    padding_right: None,
-                    data: None,
-                }]))
-            }
-        })
-        .next()
-        .await
-        .unwrap();
-    cx_a.foreground().finish_waiting();
-    cx_b.foreground().finish_waiting();
-
-    executor.run_until_parked();
-    editor_a.update(cx_a, |editor, _| {
-        assert!(
-            extract_hint_labels(editor).is_empty(),
-            "Host should get no hints due to them turned off"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            0,
-            "Turned off hints should not generate version updates"
-        );
-    });
-
-    executor.run_until_parked();
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec!["initial hint".to_string()],
-            extract_hint_labels(editor),
-            "Client should get its first hints when opens an editor"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            1,
-            "Should update cache verison after first hints"
-        );
-    });
-
-    other_hints.fetch_or(true, atomic::Ordering::Release);
-    fake_language_server
-        .request::<lsp::request::InlayHintRefreshRequest>(())
-        .await
-        .expect("inlay refresh request failed");
-    executor.run_until_parked();
-    editor_a.update(cx_a, |editor, _| {
-        assert!(
-            extract_hint_labels(editor).is_empty(),
-            "Host should get nop hints due to them turned off, even after the /refresh"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            0,
-            "Turned off hints should not generate version updates, again"
-        );
-    });
-
-    executor.run_until_parked();
-    editor_b.update(cx_b, |editor, _| {
-        assert_eq!(
-            vec!["other hint".to_string()],
-            extract_hint_labels(editor),
-            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
-        );
-        let inlay_cache = editor.inlay_hint_cache();
-        assert_eq!(
-            inlay_cache.version(),
-            2,
-            "Guest should accepted all edits and bump its cache version every time"
-        );
-    });
-}
-
-fn extract_hint_labels(editor: &Editor) -> Vec<String> {
-    let mut labels = Vec::new();
-    for hint in editor.inlay_hint_cache().hints() {
-        match hint.label {
-            project::InlayHintLabel::String(s) => labels.push(s),
-            _ => unreachable!(),
-        }
-    }
-    labels
-}

crates/collab2/src/tests/test_server.rs πŸ”—

@@ -208,11 +208,11 @@ impl TestServer {
                 })
             });
 
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http, cx));
         let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
         let mut language_registry = LanguageRegistry::test();
-        language_registry.set_executor(cx.executor().clone());
+        language_registry.set_executor(cx.executor());
         let app_state = Arc::new(workspace::AppState {
             client: client.clone(),
             user_store: user_store.clone(),

crates/copilot2/src/copilot2.rs πŸ”—

@@ -351,28 +351,29 @@ impl Copilot {
         }
     }
 
-    // #[cfg(any(test, feature = "test-support"))]
-    // pub fn fake(cx: &mut gpui::TestAppContext) -> (ModelHandle<Self>, lsp::FakeLanguageServer) {
-    //     use node_runtime::FakeNodeRuntime;
-
-    //     let (server, fake_server) =
-    //         LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
-    //     let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
-    //     let node_runtime = FakeNodeRuntime::new();
-    //     let this = cx.add_model(|_| Self {
-    //         server_id: LanguageServerId(0),
-    //         http: http.clone(),
-    //         node_runtime,
-    //         server: CopilotServer::Running(RunningCopilotServer {
-    //             name: LanguageServerName(Arc::from("copilot")),
-    //             lsp: Arc::new(server),
-    //             sign_in_status: SignInStatus::Authorized,
-    //             registered_buffers: Default::default(),
-    //         }),
-    //         buffers: Default::default(),
-    //     });
-    //     (this, fake_server)
-    // }
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
+        use node_runtime::FakeNodeRuntime;
+
+        let (server, fake_server) =
+            LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
+        let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
+        let node_runtime = FakeNodeRuntime::new();
+        let this = cx.build_model(|cx| Self {
+            server_id: LanguageServerId(0),
+            http: http.clone(),
+            node_runtime,
+            server: CopilotServer::Running(RunningCopilotServer {
+                name: LanguageServerName(Arc::from("copilot")),
+                lsp: Arc::new(server),
+                sign_in_status: SignInStatus::Authorized,
+                registered_buffers: Default::default(),
+            }),
+            _subscription: cx.on_app_quit(Self::shutdown_language_server),
+            buffers: Default::default(),
+        });
+        (this, fake_server)
+    }
 
     fn start_language_server(
         new_server_id: LanguageServerId,

crates/editor2/src/editor_tests.rs πŸ”—

@@ -11,7 +11,7 @@ use drag_and_drop::DragAndDrop;
 use futures::StreamExt;
 use gpui::{
     serde_json::{self, json},
-    TestAppContext,
+    TestAppContext, WindowOptions,
 };
 use indoc::indoc;
 use language::{
@@ -31,7 +31,7 @@ use util::{
     test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
 };
 use workspace::{
-    item::{FollowableItem, Item, ItemHandle},
+    item::{FollowableEvents, FollowableItem, Item, ItemHandle},
     NavigationEntry, ViewId,
 };
 
@@ -46,36 +46,32 @@ fn test_edit_events(cx: &mut TestAppContext) {
     });
 
     let events = Rc::new(RefCell::new(Vec::new()));
-    let editor1 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(cx.view(), move |_, _, event, _| {
-                    if matches!(event, Event::Edited | Event::BufferEdited) {
-                        events.borrow_mut().push(("editor1", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx)
-        .unwrap();
-    let editor2 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(cx.view(), move |_, _, event, _| {
-                    if matches!(event, Event::Edited | Event::BufferEdited) {
-                        events.borrow_mut().push(("editor2", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx)
-        .unwrap();
+    let editor1 = cx.add_window({
+        let events = events.clone();
+        |cx| {
+            cx.subscribe(cx.view(), move |_, _, event, _| {
+                if matches!(event, Event::Edited | Event::BufferEdited) {
+                    events.borrow_mut().push(("editor1", event.clone()));
+                }
+            })
+            .detach();
+            Editor::for_buffer(buffer.clone(), None, cx)
+        }
+    });
+
+    let editor2 = cx.add_window({
+        let events = events.clone();
+        |cx| {
+            cx.subscribe(cx.view(), move |_, _, event, _| {
+                if matches!(event, Event::Edited | Event::BufferEdited) {
+                    events.borrow_mut().push(("editor2", event.clone()));
+                }
+            })
+            .detach();
+            Editor::for_buffer(buffer.clone(), None, cx)
+        }
+    });
+
     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 
     // Mutating editor 1 will emit an `Edited` event only for that editor.
@@ -108,8 +104,6 @@ fn test_edit_events(cx: &mut TestAppContext) {
             ("editor1", Event::Edited),
             ("editor1", Event::BufferEdited),
             ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
         ]
     );
 
@@ -121,8 +115,6 @@ fn test_edit_events(cx: &mut TestAppContext) {
             ("editor1", Event::Edited),
             ("editor1", Event::BufferEdited),
             ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
         ]
     );
 
@@ -134,8 +126,6 @@ fn test_edit_events(cx: &mut TestAppContext) {
             ("editor2", Event::Edited),
             ("editor1", Event::BufferEdited),
             ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
         ]
     );
 
@@ -147,8 +137,6 @@ fn test_edit_events(cx: &mut TestAppContext) {
             ("editor2", Event::Edited),
             ("editor1", Event::BufferEdited),
             ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
         ]
     );
 
@@ -166,12 +154,10 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
     let mut now = Instant::now();
-    let buffer = cx.add_model(|cx| language::Buffer::new(0, cx.model_id() as u64, "123456"));
+    let buffer = cx.build_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
     let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 
     editor.update(cx, |editor, cx| {
         editor.start_transaction_at(now, cx);
@@ -238,14 +224,14 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 fn test_ime_composition(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let buffer = cx.add_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "abcde");
+    let buffer = cx.build_model(|cx| {
+        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde");
         // Ensure automatic grouping doesn't occur.
         buffer.set_group_interval(Duration::ZERO);
         buffer
     });
 
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
     cx.add_window(|cx| {
         let mut editor = build_editor(buffer.clone(), cx);
 
@@ -341,55 +327,64 @@ fn test_ime_composition(cx: &mut TestAppContext) {
 fn test_selection_with_mouse(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
+        build_editor(buffer, cx)
+    });
+
     editor.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
     });
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
     );
 
     editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(3, 3), 0, gpui::Point::<f32>::zero(), cx);
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
     );
 
     editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(1, 1), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(1, 1), 0, gpui::Point::<f32>::zero(), cx);
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
     );
 
     editor.update(cx, |view, cx| {
         view.end_selection(cx);
-        view.update_selection(DisplayPoint::new(3, 3), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(3, 3), 0, gpui::Point::<f32>::zero(), cx);
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
     );
 
     editor.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 0), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(0, 0), 0, gpui::Point::<f32>::zero(), cx);
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [
             DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
             DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
@@ -401,7 +396,9 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
     });
 
     assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+        editor
+            .update(cx, |view, cx| view.selections.display_ranges(cx))
+            .unwrap(),
         [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
     );
 }
@@ -410,12 +407,10 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
 fn test_canceling_pending_selection(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
@@ -426,7 +421,7 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
     });
 
     view.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(3, 3), 0, gpui::Point::<f32>::zero(), cx);
         assert_eq!(
             view.selections.display_ranges(cx),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
@@ -435,7 +430,7 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 
     view.update(cx, |view, cx| {
         view.cancel(&Cancel, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(DisplayPoint::new(1, 1), 0, gpui::Point::<f32>::zero(), cx);
         assert_eq!(
             view.selections.display_ranges(cx),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
@@ -458,12 +453,10 @@ fn test_clone(cx: &mut TestAppContext) {
         true,
     );
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&text, cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&text, cx);
+        build_editor(buffer, cx)
+    });
 
     editor.update(cx, |editor, cx| {
         editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
@@ -477,11 +470,9 @@ fn test_clone(cx: &mut TestAppContext) {
         );
     });
 
-    let cloned_editor = editor
-        .update(cx, |editor, cx| {
-            cx.add_window(Default::default(), |cx| editor.clone(cx))
-        })
-        .root(cx);
+    let cloned_editor = editor.update(cx, |editor, cx| {
+        cx.add_window(Default::default(), |cx| editor.clone(cx))
+    });
 
     let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
     let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
@@ -513,11 +504,12 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
     cx.set_global(DragAndDrop::<Workspace>::default());
     use workspace::item::Item;
 
-    let fs = FakeFs::new(cx.background());
+    let fs = FakeFs::new(cx.executor());
     let project = Project::test(fs, [], cx).await;
     let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-    let workspace = window.root(cx);
+    let workspace = window;
     let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
+
     window.add_view(cx, |cx| {
         let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
         let mut editor = build_editor(buffer.clone(), cx);
@@ -614,7 +606,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
         );
         assert_eq!(
             editor.scroll_position(cx),
-            vec2f(0., editor.max_point(cx).row() as f32)
+            Point::new(0., editor.max_point(cx).row() as f32)
         );
 
         editor
@@ -625,20 +617,28 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 fn test_cancel(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(1, 1),
+            0,
+            gpui::Point::<Pixels>::zero(),
+            cx,
+        );
         view.end_selection(cx);
 
         view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 3), 0, Point::<Pixels>::zero(), cx);
+        view.update_selection(
+            DisplayPoint::new(0, 3),
+            0,
+            gpui::Point::<Pixels>::zero(),
+            cx,
+        );
         view.end_selection(cx);
         assert_eq!(
             view.selections.display_ranges(cx),
@@ -670,10 +670,9 @@ fn test_cancel(cx: &mut TestAppContext) {
 fn test_fold_action(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(
-                &"
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(
+            &"
                 impl Foo {
                     // Hello!
 
@@ -690,12 +689,11 @@ fn test_fold_action(cx: &mut TestAppContext) {
                     }
                 }
             "
-                .unindent(),
-                cx,
-            );
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+            .unindent(),
+            cx,
+        );
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -763,9 +761,7 @@ fn test_move_cursor(cx: &mut TestAppContext) {
     init_test(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))
-        .root(cx);
+    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 
     buffer.update(cx, |buffer, cx| {
         buffer.edit(
@@ -840,12 +836,10 @@ fn test_move_cursor(cx: &mut TestAppContext) {
 fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδΡ", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδΡ", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     assert_eq!('ⓐ'.len_utf8(), 3);
     assert_eq!('Ξ±'.len_utf8(), 2);
@@ -958,12 +952,10 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nΞ±Ξ²Ξ³\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nΞ±Ξ²Ξ³\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
@@ -1010,12 +1002,10 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 fn test_beginning_end_of_line(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\n  def", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -1175,12 +1165,10 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -1229,13 +1217,10 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer =
-                MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.set_wrap_width(Some(140.), cx);
@@ -1293,7 +1278,7 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon
 
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
     let window = cx.window;
-    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
+    window.simulate_resize(Point::new(100., 4. * line_height), &mut cx);
 
     cx.set_state(
         &r#"Λ‡one
@@ -1392,7 +1377,7 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
     let mut cx = EditorTestContext::new(cx).await;
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
     let window = cx.window;
-    window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
+    window.simulate_resize(Point::new(1000., 4. * line_height + 0.5), &mut cx);
 
     cx.set_state(
         &r#"Λ‡one
@@ -1409,18 +1394,18 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
     );
 
     cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 0.));
         editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 3.));
         editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 6.));
         editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 3.));
 
         editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 1.));
         editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 3.));
     });
 }
 
@@ -1435,7 +1420,7 @@ async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
     });
 
     let window = cx.window;
-    window.simulate_resize(vec2f(1000., 6.0 * line_height), &mut cx);
+    window.simulate_resize(Point::new(1000., 6.0 * line_height), &mut cx);
 
     cx.set_state(
         &r#"Λ‡one
@@ -1451,7 +1436,7 @@ async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
         "#,
     );
     cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.0));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 0.0));
     });
 
     // Add a cursor below the visible area. Since both cursors cannot fit
@@ -1466,7 +1451,7 @@ async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
         })
     });
     cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 3.0));
     });
 
     // Move down. The editor cursor scrolls down to track the newest cursor.
@@ -1474,7 +1459,7 @@ async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
         editor.move_down(&Default::default(), cx);
     });
     cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 4.0));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 4.0));
     });
 
     // Add a cursor above the visible area. Since both cursors fit on screen,
@@ -1488,7 +1473,7 @@ async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
         })
     });
     cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.0));
+        assert_eq!(editor.snapshot(cx).scroll_position(), Point::new(0., 1.0));
     });
 }
 
@@ -1499,7 +1484,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
     let window = cx.window;
-    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
+    window.simulate_resize(Point::new(100., 4. * line_height), &mut cx);
 
     cx.set_state(
         &r#"
@@ -1623,12 +1608,10 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("one two three four", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("one two three four", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -1661,12 +1644,10 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 fn test_newline(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -1686,10 +1667,9 @@ fn test_newline(cx: &mut TestAppContext) {
 fn test_newline_with_old_selections(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(
-                "
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(
+            "
                 a
                 b(
                     X
@@ -1698,20 +1678,19 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
                     X
                 )
             "
-                .unindent()
-                .as_str(),
-                cx,
-            );
-            let mut editor = build_editor(buffer.clone(), cx);
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([
-                    Point::new(2, 4)..Point::new(2, 5),
-                    Point::new(5, 4)..Point::new(5, 5),
-                ])
-            });
-            editor
-        })
-        .root(cx);
+            .unindent()
+            .as_str(),
+            cx,
+        );
+        let mut editor = build_editor(buffer.clone(), cx);
+        editor.change_selections(None, cx, |s| {
+            s.select_ranges([
+                Point::new(2, 4)..Point::new(2, 5),
+                Point::new(5, 4)..Point::new(5, 5),
+            ])
+        });
+        editor
+    });
 
     editor.update(cx, |editor, cx| {
         // Edit the buffer directly, deleting ranges surrounding the editor's selections
@@ -1916,14 +1895,12 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 fn test_insert_with_old_selections(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", 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
-        })
-        .root(cx);
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", 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
+    });
 
     editor.update(cx, |editor, cx| {
         // Edit the buffer directly, deleting ranges surrounding the editor's selections
@@ -2271,14 +2248,14 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
         None,
     ));
 
-    let toml_buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n").with_language(toml_language, cx)
+    let toml_buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n").with_language(toml_language, cx)
     });
-    let rust_buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, "const c: usize = 3;\n")
+    let rust_buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), "const c: usize = 3;\n")
             .with_language(rust_language, cx)
     });
-    let multibuffer = cx.add_model(|cx| {
+    let multibuffer = cx.build_model(|cx| {
         let mut multibuffer = MultiBuffer::new(0);
         multibuffer.push_excerpts(
             toml_buffer.clone(),
@@ -2432,12 +2409,10 @@ async fn test_delete(cx: &mut gpui::TestAppContext) {
 fn test_delete_line(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2457,12 +2432,10 @@ fn test_delete_line(cx: &mut TestAppContext) {
         );
     });
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
@@ -2867,12 +2840,10 @@ async fn test_manipulate_text(cx: &mut TestAppContext) {
 fn test_duplicate_line(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2895,12 +2866,10 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         );
     });
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2924,12 +2893,10 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
 fn test_move_line_up_down(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
@@ -3025,12 +2992,10 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
 fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let editor = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     editor.update(cx, |editor, cx| {
         let snapshot = editor.buffer.read(cx).snapshot(cx);
         editor.insert_blocks(
@@ -3345,12 +3310,10 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 fn test_select_all(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.select_all(&SelectAll, cx);
         assert_eq!(
@@ -3364,12 +3327,10 @@ fn test_select_all(cx: &mut TestAppContext) {
 fn test_select_line(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -3413,12 +3374,10 @@ fn test_select_line(cx: &mut TestAppContext) {
 fn test_split_selection_into_lines(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
@@ -3486,12 +3445,10 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 fn test_add_selection_above_below(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -3786,10 +3743,11 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+    });
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let view = cx.add_window(|cx| build_editor(buffer, cx));
     view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
 
@@ -3950,10 +3908,11 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 
     let text = "fn a() {}";
 
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+    });
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer, cx));
     editor
         .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
         .await;
@@ -4514,10 +4473,11 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+    });
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let view = cx.add_window(|cx| build_editor(buffer, cx));
     view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
 
@@ -4663,10 +4623,11 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| {
+        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+    });
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer, cx));
     editor
         .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
@@ -4756,7 +4717,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) {
     );
 
     let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let editor = cx.add_window(|cx| build_editor(buffer, cx));
 
     editor.update(cx, |editor, cx| {
         let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
@@ -4872,7 +4833,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.background());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_file("/file.rs", Default::default()).await;
 
     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
@@ -4885,8 +4846,8 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
     cx.foreground().start_waiting();
     let fake_server = fake_servers.next().await.unwrap();
 
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 
@@ -4984,7 +4945,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.background());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_file("/file.rs", Default::default()).await;
 
     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
@@ -4997,8 +4958,8 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
     cx.foreground().start_waiting();
     let fake_server = fake_servers.next().await.unwrap();
 
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+    let editor = cx.add_window(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 

crates/editor2/src/test.rs πŸ”—

@@ -60,6 +60,7 @@ pub fn assert_text_with_selections(
 #[allow(dead_code)]
 #[cfg(any(test, feature = "test-support"))]
 pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
+    // todo!()
     Editor::new(EditorMode::Full, buffer, None, /*None,*/ cx)
 }
 
@@ -68,5 +69,6 @@ pub(crate) fn build_editor_with_project(
     buffer: Model<MultiBuffer>,
     cx: &mut ViewContext<Editor>,
 ) -> Editor {
+    // todo!()
     Editor::new(EditorMode::Full, buffer, Some(project), /*None,*/ cx)
 }

crates/editor2/src/test/editor_test_context.rs πŸ”—

@@ -27,7 +27,7 @@ pub struct EditorTestContext<'a> {
 
 impl<'a> EditorTestContext<'a> {
     pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         // fs.insert_file("/file", "".to_owned()).await;
         fs.insert_tree(
             "/root",

crates/gpui2/src/app.rs πŸ”—

@@ -1008,11 +1008,14 @@ impl Context for AppContext {
         read(entity, self)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
         let window = self
             .windows
             .get(window.id)
@@ -1021,7 +1024,11 @@ impl Context for AppContext {
             .unwrap();
 
         let root_view = window.root_view.clone().unwrap();
-        Ok(read(root_view, self))
+        let view = root_view
+            .downcast::<T>()
+            .map_err(|_| anyhow!("root view's type has changed"))?;
+
+        Ok(read(view.read(self), self))
     }
 }
 

crates/gpui2/src/app/async_context.rs πŸ”—

@@ -67,11 +67,14 @@ impl Context for AsyncAppContext {
         lock.update_window(window, f)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
         let app = self.app.upgrade().context("app was released")?;
         let lock = app.borrow();
         lock.read_window(window, read)
@@ -261,11 +264,14 @@ impl Context for AsyncWindowContext {
         self.app.read_model(handle, read)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
         self.app.read_window(window, read)
     }
 }

crates/gpui2/src/app/model_context.rs πŸ”—

@@ -1,6 +1,6 @@
 use crate::{
     AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
-    EventEmitter, Model, Subscription, Task, WeakModel, WindowContext,
+    EventEmitter, Model, Subscription, Task, WeakModel, WindowContext, WindowHandle,
 };
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
@@ -240,11 +240,14 @@ impl<'a, T> Context for ModelContext<'a, T> {
         self.app.read_model(handle, read)
     }
 
-    fn read_window<R>(
+    fn read_window<U, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<U>,
+        read: impl FnOnce(&U, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        U: 'static,
+    {
         self.app.read_window(window, read)
     }
 }

crates/gpui2/src/app/test_context.rs πŸ”—

@@ -59,11 +59,14 @@ impl Context for TestAppContext {
         app.read_model(handle, read)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
         let app = self.app.borrow();
         app.read_window(window, read)
     }
@@ -102,8 +105,8 @@ impl TestAppContext {
         Ok(())
     }
 
-    pub fn executor(&self) -> &BackgroundExecutor {
-        &self.background_executor
+    pub fn executor(&self) -> BackgroundExecutor {
+        self.background_executor.clone()
     }
 
     pub fn foreground_executor(&self) -> &ForegroundExecutor {

crates/gpui2/src/color.rs πŸ”—

@@ -154,6 +154,30 @@ impl Hsla {
     pub fn to_rgb(self) -> Rgba {
         self.into()
     }
+
+    pub fn red() -> Self {
+        red()
+    }
+
+    pub fn green() -> Self {
+        green()
+    }
+
+    pub fn blue() -> Self {
+        blue()
+    }
+
+    pub fn black() -> Self {
+        black()
+    }
+
+    pub fn white() -> Self {
+        white()
+    }
+
+    pub fn transparent_black() -> Self {
+        transparent_black()
+    }
 }
 
 impl Eq for Hsla {}
@@ -212,6 +236,15 @@ pub fn blue() -> Hsla {
     }
 }
 
+pub fn green() -> Hsla {
+    Hsla {
+        h: 0.3,
+        s: 1.,
+        l: 0.5,
+        a: 1.,
+    }
+}
+
 impl Hsla {
     /// Returns true if the HSLA color is fully transparent, false otherwise.
     pub fn is_transparent(&self) -> bool {

crates/gpui2/src/gpui2.rs πŸ”—

@@ -105,11 +105,13 @@ pub trait Context {
     where
         F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R>;
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static;
 }
 
 pub trait VisualContext: Context {

crates/gpui2/src/window.rs πŸ”—

@@ -10,7 +10,7 @@ use crate::{
     SharedString, Size, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline,
     UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
 };
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Context as _, Result};
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
 use futures::{
@@ -1429,16 +1429,25 @@ impl Context for WindowContext<'_> {
         read(&*entity, &*self.app)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
-        if window == &self.window.handle {
-            let root_view = self.window.root_view.clone().unwrap();
-            Ok(read(root_view, self))
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
+        if window.any_handle == self.window.handle {
+            let root_view = self
+                .window
+                .root_view
+                .clone()
+                .unwrap()
+                .downcast::<T>()
+                .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
+            Ok(read(root_view.read(self), self))
         } else {
-            window.read(self.app, read)
+            self.app.read_window(window, read)
         }
     }
 }
@@ -2257,11 +2266,14 @@ impl<V> Context for ViewContext<'_, V> {
         self.window_cx.read_model(handle, read)
     }
 
-    fn read_window<R>(
+    fn read_window<T, R>(
         &self,
-        window: &AnyWindowHandle,
-        read: impl FnOnce(AnyView, &AppContext) -> R,
-    ) -> Result<R> {
+        window: &WindowHandle<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
         self.window_cx.read_window(window, read)
     }
 }
@@ -2335,14 +2347,6 @@ impl<V: 'static + Render> WindowHandle<V> {
         }
     }
 
-    pub fn root<C: Context>(&self, cx: &C) -> Result<View<V>> {
-        cx.read_window(&self.any_handle, |root_view, _| {
-            root_view
-                .downcast::<V>()
-                .map_err(|_| anyhow!("the type of the window's root view has changed"))
-        })?
-    }
-
     pub fn update<C, R>(
         self,
         cx: &mut C,
@@ -2358,6 +2362,29 @@ impl<V: 'static + Render> WindowHandle<V> {
             Ok(cx.update_view(&view, update))
         })?
     }
+
+    pub fn read<'a>(&self, cx: &'a AppContext) -> Result<&'a V> {
+        let x = cx
+            .windows
+            .get(self.id)
+            .and_then(|window| {
+                window
+                    .as_ref()
+                    .and_then(|window| window.root_view.clone())
+                    .map(|root_view| root_view.downcast::<V>())
+            })
+            .ok_or_else(|| anyhow!("window not found"))?
+            .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
+
+        Ok(x.read(cx))
+    }
+
+    pub fn read_with<C, R>(self, cx: &C, read_with: impl FnOnce(&V, &AppContext) -> R) -> Result<R>
+    where
+        C: Context,
+    {
+        cx.read_window(&self, |root_view: &V, cx| read_with(root_view, cx))
+    }
 }
 
 impl<V> Copy for WindowHandle<V> {}
@@ -2424,11 +2451,16 @@ impl AnyWindowHandle {
         cx.update_window(self, update)
     }
 
-    pub fn read<C, R>(self, cx: &C, read: impl FnOnce(AnyView, &AppContext) -> R) -> Result<R>
+    pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(&T, &AppContext) -> R) -> Result<R>
     where
         C: Context,
+        T: 'static,
     {
-        cx.read_window(&self, read)
+        let view = self
+            .downcast::<T>()
+            .context("the type of the window's root view has changed")?;
+
+        cx.read_window(&view, read)
     }
 }
 

crates/language2/src/language2.rs πŸ”—

@@ -1858,7 +1858,7 @@ mod tests {
     async fn test_first_line_pattern(cx: &mut TestAppContext) {
         let mut languages = LanguageRegistry::test();
 
-        languages.set_executor(cx.executor().clone());
+        languages.set_executor(cx.executor());
         let languages = Arc::new(languages);
         languages.register(
             "/javascript",
@@ -1895,7 +1895,7 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_language_loading(cx: &mut TestAppContext) {
         let mut languages = LanguageRegistry::test();
-        languages.set_executor(cx.executor().clone());
+        languages.set_executor(cx.executor());
         let languages = Arc::new(languages);
         languages.register(
             "/JSON",

crates/prettier2/src/prettier2.rs πŸ”—

@@ -490,7 +490,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_prettier_lookup_finds_nothing(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/root",
             json!({
@@ -563,7 +563,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_prettier_lookup_in_simple_npm_projects(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/root",
             json!({
@@ -628,7 +628,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_prettier_lookup_for_not_installed(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/root",
             json!({
@@ -686,7 +686,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_prettier_lookup_in_npm_workspaces(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/root",
             json!({
@@ -742,7 +742,7 @@ mod tests {
     async fn test_prettier_lookup_in_npm_workspaces_for_not_installed(
         cx: &mut gpui::TestAppContext,
     ) {
-        let fs = FakeFs::new(cx.executor().clone());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/root",
             json!({

crates/project2/src/project2.rs πŸ”—

@@ -863,7 +863,7 @@ impl Project {
         cx: &mut gpui::TestAppContext,
     ) -> Model<Project> {
         let mut languages = LanguageRegistry::test();
-        languages.set_executor(cx.executor().clone());
+        languages.set_executor(cx.executor());
         let http_client = util::http::FakeHttpClient::with_404_response();
         let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
         let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http_client, cx));

crates/project2/src/project_tests.rs πŸ”—

@@ -89,7 +89,7 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) {
 async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/the-root",
         json!({
@@ -189,7 +189,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/the-root",
         json!({
@@ -547,7 +547,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/the-root",
         json!({
@@ -734,7 +734,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
 async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -826,7 +826,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
 async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/root",
         json!({
@@ -914,7 +914,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -1046,7 +1046,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
 
     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
@@ -1125,7 +1125,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": "x" })).await;
 
     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
@@ -1215,7 +1215,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
 
     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
@@ -1279,7 +1279,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
         .await;
 
@@ -1401,7 +1401,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
     "
     .unindent();
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": text })).await;
 
     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
@@ -1671,7 +1671,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
         "let three = 3;\n",
     );
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": text })).await;
 
     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
@@ -1734,7 +1734,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
 async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({ "a.rs": "one two three" }))
         .await;
 
@@ -1813,7 +1813,7 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
     "
     .unindent();
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -1959,7 +1959,7 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui::TestAp
     "
     .unindent();
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2067,7 +2067,7 @@ async fn test_invalid_edits_from_lsp2(cx: &mut gpui::TestAppContext) {
     "
     .unindent();
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2187,7 +2187,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
     );
     let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2299,7 +2299,7 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2396,7 +2396,7 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2451,7 +2451,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
     );
     let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2559,7 +2559,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
 async fn test_save_file(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2591,7 +2591,7 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) {
 async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2622,7 +2622,7 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
 async fn test_save_as(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree("/dir", json!({})).await;
 
     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
@@ -2830,7 +2830,7 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
 async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2881,7 +2881,7 @@ async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) {
 async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -2927,7 +2927,7 @@ async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
 async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3074,7 +3074,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let initial_contents = "aaa\nbbbbb\nc\n";
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3154,7 +3154,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
 async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3216,7 +3216,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
 async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/the-dir",
         json!({
@@ -3479,7 +3479,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
         }))
         .await;
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3596,7 +3596,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
 async fn test_search(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3655,7 +3655,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
 
     let search_query = "file";
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3767,7 +3767,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
 
     let search_query = "file";
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({
@@ -3878,7 +3878,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
 
     let search_query = "file";
 
-    let fs = FakeFs::new(cx.executor().clone());
+    let fs = FakeFs::new(cx.executor());
     fs.insert_tree(
         "/dir",
         json!({

crates/rpc2/src/peer.rs πŸ”—

@@ -577,18 +577,18 @@ mod tests {
         let client2 = Peer::new(0);
 
         let (client1_to_server_conn, server_to_client_1_conn, _kill) =
-            Connection::in_memory(cx.executor().clone());
+            Connection::in_memory(cx.executor());
         let (client1_conn_id, io_task1, client1_incoming) =
-            client1.add_test_connection(client1_to_server_conn, cx.executor().clone());
+            client1.add_test_connection(client1_to_server_conn, cx.executor());
         let (_, io_task2, server_incoming1) =
-            server.add_test_connection(server_to_client_1_conn, cx.executor().clone());
+            server.add_test_connection(server_to_client_1_conn, cx.executor());
 
         let (client2_to_server_conn, server_to_client_2_conn, _kill) =
-            Connection::in_memory(cx.executor().clone());
+            Connection::in_memory(cx.executor());
         let (client2_conn_id, io_task3, client2_incoming) =
-            client2.add_test_connection(client2_to_server_conn, cx.executor().clone());
+            client2.add_test_connection(client2_to_server_conn, cx.executor());
         let (_, io_task4, server_incoming2) =
-            server.add_test_connection(server_to_client_2_conn, cx.executor().clone());
+            server.add_test_connection(server_to_client_2_conn, cx.executor());
 
         executor.spawn(io_task1).detach();
         executor.spawn(io_task2).detach();
@@ -763,16 +763,16 @@ mod tests {
 
     #[gpui::test(iterations = 50)]
     async fn test_dropping_request_before_completion(cx: &mut TestAppContext) {
-        let executor = cx.executor().clone();
+        let executor = cx.executor();
         let server = Peer::new(0);
         let client = Peer::new(0);
 
         let (client_to_server_conn, server_to_client_conn, _kill) =
-            Connection::in_memory(cx.executor().clone());
+            Connection::in_memory(cx.executor());
         let (client_to_server_conn_id, io_task1, mut client_incoming) =
-            client.add_test_connection(client_to_server_conn, cx.executor().clone());
+            client.add_test_connection(client_to_server_conn, cx.executor());
         let (server_to_client_conn_id, io_task2, mut server_incoming) =
-            server.add_test_connection(server_to_client_conn, cx.executor().clone());
+            server.add_test_connection(server_to_client_conn, cx.executor());
 
         executor.spawn(io_task1).detach();
         executor.spawn(io_task2).detach();

crates/workspace2/src/workspace2.rs πŸ”—

@@ -3443,27 +3443,27 @@ impl Workspace {
         })
     }
 
-    // todo!()
-    //     #[cfg(any(test, feature = "test-support"))]
-    //     pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
-    //         use node_runtime::FakeNodeRuntime;
-
-    //         let client = project.read(cx).client();
-    //         let user_store = project.read(cx).user_store();
-
-    //         let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
-    //         let app_state = Arc::new(AppState {
-    //             languages: project.read(cx).languages().clone(),
-    //             workspace_store,
-    //             client,
-    //             user_store,
-    //             fs: project.read(cx).fs().clone(),
-    //             build_window_options: |_, _, _| Default::default(),
-    //             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
-    //             node_runtime: FakeNodeRuntime::new(),
-    //         });
-    //         Self::new(0, project, app_state, cx)
-    //     }
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
+        use gpui::Context;
+        use node_runtime::FakeNodeRuntime;
+
+        let client = project.read(cx).client();
+        let user_store = project.read(cx).user_store();
+
+        let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let app_state = Arc::new(AppState {
+            languages: project.read(cx).languages().clone(),
+            workspace_store,
+            client,
+            user_store,
+            fs: project.read(cx).fs().clone(),
+            build_window_options: |_, _, _| Default::default(),
+            initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
+            node_runtime: FakeNodeRuntime::new(),
+        });
+        Self::new(0, project, app_state, cx)
+    }
 
     //     fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
     //         let dock = match position {