Restructure fake language server setup

Max Brunsfeld created

Replace FakeLanguageServerConfig with FakeLanguageServerAdapter

Change summary

crates/editor/src/editor.rs     |  51 +-
crates/language/src/language.rs | 101 +++-
crates/project/src/project.rs   | 124 ++---
crates/server/src/rpc.rs        | 715 ++++++++++++++++------------------
4 files changed, 496 insertions(+), 495 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -6438,13 +6438,12 @@ pub fn styled_runs_for_code_label<'a>(
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
     use gpui::{
         geometry::rect::RectF,
         platform::{WindowBounds, WindowOptions},
     };
-    use language::{LanguageConfig, LanguageServerConfig};
+    use language::{FakeLspAdapter, LanguageConfig};
     use lsp::FakeLanguageServer;
     use project::FakeFs;
     use smol::stream::StreamExt;
@@ -8880,26 +8879,27 @@ mod tests {
         cx.foreground().forbid_parking();
         cx.update(populate_settings);
 
-        let (mut language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-        language_server_config.set_fake_capabilities(lsp::ServerCapabilities {
-            document_formatting_provider: Some(lsp::OneOf::Left(true)),
-            ..Default::default()
-        });
-        let language = Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                document_formatting_provider: Some(lsp::OneOf::Left(true)),
+                ..Default::default()
+            },
+            ..Default::default()
+        });
 
         let fs = FakeFs::new(cx.background().clone());
         fs.insert_file("/file.rs", Default::default()).await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| project.languages().add(language));
+        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
 
         let worktree_id = project
             .update(cx, |project, cx| {
@@ -8913,6 +8913,8 @@ mod tests {
             .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
             .await
             .unwrap();
+
+        cx.foreground().start_waiting();
         let mut fake_server = fake_servers.next().await.unwrap();
 
         let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
@@ -8934,6 +8936,7 @@ mod tests {
             })
             .next()
             .await;
+        cx.foreground().start_waiting();
         save.await.unwrap();
         assert_eq!(
             editor.read_with(cx, |editor, cx| editor.text(cx)),
@@ -8955,6 +8958,7 @@ mod tests {
         });
         let save = cx.update(|cx| editor.save(project.clone(), cx));
         cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
+        cx.foreground().start_waiting();
         save.await.unwrap();
         assert_eq!(
             editor.read_with(cx, |editor, cx| editor.text(cx)),
@@ -8967,23 +8971,24 @@ mod tests {
     async fn test_completion(cx: &mut gpui::TestAppContext) {
         cx.update(populate_settings);
 
-        let (mut language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-        language_server_config.set_fake_capabilities(lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        });
-        let language = Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
+                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        });
 
         let text = "
             one
@@ -8996,7 +9001,7 @@ mod tests {
         fs.insert_file("/file.rs", text).await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| project.languages().add(language));
+        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
 
         let worktree_id = project
             .update(cx, |project, cx| {

crates/language/src/language.rs 🔗

@@ -138,16 +138,13 @@ impl Default for LanguageConfig {
 pub struct LanguageServerConfig {
     pub disk_based_diagnostic_sources: HashSet<String>,
     pub disk_based_diagnostics_progress_token: Option<String>,
-    #[cfg(any(test, feature = "test-support"))]
-    #[serde(skip)]
-    fake_config: Option<FakeLanguageServerConfig>,
 }
 
 #[cfg(any(test, feature = "test-support"))]
-struct FakeLanguageServerConfig {
-    servers_tx: mpsc::UnboundedSender<lsp::FakeLanguageServer>,
-    capabilities: lsp::ServerCapabilities,
-    initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
+pub struct FakeLspAdapter {
+    pub name: &'static str,
+    pub capabilities: lsp::ServerCapabilities,
+    pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
 }
 
 #[derive(Clone, Debug, Deserialize)]
@@ -162,6 +159,12 @@ pub struct Language {
     pub(crate) config: LanguageConfig,
     pub(crate) grammar: Option<Arc<Grammar>>,
     pub(crate) adapter: Option<Arc<dyn LspAdapter>>,
+
+    #[cfg(any(test, feature = "test-support"))]
+    fake_adapter: Option<(
+        mpsc::UnboundedSender<lsp::FakeLanguageServer>,
+        Arc<FakeLspAdapter>,
+    )>,
 }
 
 pub struct Grammar {
@@ -262,26 +265,22 @@ impl LanguageRegistry {
         cx: &mut MutableAppContext,
     ) -> Option<Task<Result<lsp::LanguageServer>>> {
         #[cfg(any(test, feature = "test-support"))]
-        if language.config.language_server.fake_config.is_some() {
+        if language.fake_adapter.is_some() {
             let language = language.clone();
             return Some(cx.spawn(|mut cx| async move {
-                let fake_config = language
-                    .config
-                    .language_server
-                    .fake_config
-                    .as_ref()
-                    .unwrap();
+                let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap();
                 let (server, mut fake_server) = cx.update(|cx| {
                     lsp::LanguageServer::fake_with_capabilities(
-                        fake_config.capabilities.clone(),
+                        fake_adapter.capabilities.clone(),
                         cx,
                     )
                 });
-                if let Some(initializer) = &fake_config.initializer {
+
+                if let Some(initializer) = &fake_adapter.initializer {
                     initializer(&mut fake_server);
                 }
 
-                let servers_tx = fake_config.servers_tx.clone();
+                let servers_tx = servers_tx.clone();
                 cx.background()
                     .spawn(async move {
                         fake_server
@@ -426,6 +425,9 @@ impl Language {
                 })
             }),
             adapter: None,
+
+            #[cfg(any(test, feature = "test-support"))]
+            fake_adapter: None,
         }
     }
 
@@ -478,6 +480,18 @@ impl Language {
         self
     }
 
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn set_fake_lsp_adapter(
+        &mut self,
+        fake_lsp_adapter: FakeLspAdapter,
+    ) -> mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
+        let (servers_tx, servers_rx) = mpsc::unbounded();
+        let adapter = Arc::new(fake_lsp_adapter);
+        self.fake_adapter = Some((servers_tx, adapter.clone()));
+        self.adapter = Some(adapter);
+        servers_rx
+    }
+
     pub fn name(&self) -> Arc<str> {
         self.config.name.clone()
     }
@@ -601,33 +615,42 @@ impl CodeLabel {
 }
 
 #[cfg(any(test, feature = "test-support"))]
-impl LanguageServerConfig {
-    pub fn fake() -> (Self, mpsc::UnboundedReceiver<lsp::FakeLanguageServer>) {
-        let (servers_tx, servers_rx) = mpsc::unbounded();
-        (
-            Self {
-                fake_config: Some(FakeLanguageServerConfig {
-                    servers_tx,
-                    capabilities: lsp::LanguageServer::full_capabilities(),
-                    initializer: None,
-                }),
-                disk_based_diagnostics_progress_token: Some("fakeServer/check".to_string()),
-                ..Default::default()
-            },
-            servers_rx,
-        )
+impl Default for FakeLspAdapter {
+    fn default() -> Self {
+        Self {
+            name: "the-fake-language-server",
+            capabilities: lsp::LanguageServer::full_capabilities(),
+            initializer: None,
+        }
     }
+}
 
-    pub fn set_fake_capabilities(&mut self, capabilities: lsp::ServerCapabilities) {
-        self.fake_config.as_mut().unwrap().capabilities = capabilities;
+impl LspAdapter for FakeLspAdapter {
+    fn name(&self) -> LanguageServerName {
+        LanguageServerName(self.name.into())
     }
 
-    pub fn set_fake_initializer(
-        &mut self,
-        initializer: impl 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer),
-    ) {
-        self.fake_config.as_mut().unwrap().initializer = Some(Box::new(initializer));
+    fn fetch_latest_server_version(
+        &self,
+        _: Arc<dyn HttpClient>,
+    ) -> BoxFuture<'static, Result<Box<dyn 'static + Send + Any>>> {
+        unreachable!();
     }
+
+    fn fetch_server_binary(
+        &self,
+        _: Box<dyn 'static + Send + Any>,
+        _: Arc<dyn HttpClient>,
+        _: PathBuf,
+    ) -> BoxFuture<'static, Result<PathBuf>> {
+        unreachable!();
+    }
+
+    fn cached_server_binary(&self, _: PathBuf) -> BoxFuture<'static, Option<PathBuf>> {
+        unreachable!();
+    }
+
+    fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
 }
 
 impl ToLspPosition for PointUtf16 {

crates/project/src/project.rs 🔗

@@ -4606,8 +4606,8 @@ mod tests {
     use futures::StreamExt;
     use gpui::test::subscribe;
     use language::{
-        tree_sitter_rust, Diagnostic, LanguageConfig, LanguageServerConfig, OffsetRangeExt, Point,
-        ToPoint,
+        tree_sitter_rust, Diagnostic, FakeLspAdapter, LanguageConfig, LanguageServerConfig,
+        OffsetRangeExt, Point, ToPoint,
     };
     use lsp::Url;
     use serde_json::json;
@@ -4683,41 +4683,44 @@ mod tests {
     async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
         cx.foreground().forbid_parking();
 
-        let (mut rust_lsp_config, mut fake_rust_servers) = LanguageServerConfig::fake();
-        let (mut json_lsp_config, mut fake_json_servers) = LanguageServerConfig::fake();
-        rust_lsp_config.set_fake_capabilities(lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        });
-        json_lsp_config.set_fake_capabilities(lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![":".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        });
-
-        let rust_language = Arc::new(Language::new(
+        let mut rust_language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: rust_lsp_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
-        let json_language = Arc::new(Language::new(
+        );
+        let mut json_language = Language::new(
             LanguageConfig {
                 name: "JSON".into(),
                 path_suffixes: vec!["json".to_string()],
-                language_server: json_lsp_config,
                 ..Default::default()
             },
             None,
-        ));
+        );
+        let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(FakeLspAdapter {
+            name: "the-rust-language-server",
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
+                    trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        });
+        let mut fake_json_servers = json_language.set_fake_lsp_adapter(FakeLspAdapter {
+            name: "the-json-language-server",
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
+                    trigger_characters: Some(vec![":".to_string()]),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        });
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -4733,8 +4736,8 @@ mod tests {
 
         let project = Project::test(fs, cx);
         project.update(cx, |project, _| {
-            project.languages.add(rust_language);
-            project.languages.add(json_language);
+            project.languages.add(Arc::new(rust_language));
+            project.languages.add(Arc::new(json_language));
         });
 
         let worktree_id = project
@@ -4903,21 +4906,20 @@ mod tests {
     async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
         cx.foreground().forbid_parking();
 
-        let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-        let progress_token = language_server_config
-            .disk_based_diagnostics_progress_token
-            .clone()
-            .unwrap();
-
-        let language = Arc::new(Language::new(
+        let progress_token = "the-progress-token".to_string();
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
+                language_server: LanguageServerConfig {
+                    disk_based_diagnostics_progress_token: Some(progress_token.clone()),
+                    ..Default::default()
+                },
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -4930,7 +4932,7 @@ mod tests {
         .await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| project.languages.add(language));
+        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 
         let (tree, _) = project
             .update(cx, |project, cx| {
@@ -5022,19 +5024,20 @@ mod tests {
     async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
         cx.foreground().forbid_parking();
 
-        let (mut lsp_config, mut fake_servers) = LanguageServerConfig::fake();
-        lsp_config
-            .disk_based_diagnostic_sources
-            .insert("disk".to_string());
-        let language = Arc::new(Language::new(
+        // let (mut lsp_config, mut fake_servers) = LanguageServerConfig::fake();
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: lsp_config,
+                language_server: LanguageServerConfig {
+                    disk_based_diagnostic_sources: ["disk".to_string()].into_iter().collect(),
+                    ..Default::default()
+                },
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
 
         let text = "
             fn a() { A }
@@ -5047,7 +5050,7 @@ mod tests {
         fs.insert_tree("/dir", json!({ "a.rs": text })).await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| project.languages.add(language));
+        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 
         let worktree_id = project
             .update(cx, |project, cx| {
@@ -5396,16 +5399,15 @@ mod tests {
     async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) {
         cx.foreground().forbid_parking();
 
-        let (lsp_config, mut fake_servers) = LanguageServerConfig::fake();
-        let language = Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: lsp_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
 
         let text = "
             fn a() {
@@ -5430,7 +5432,7 @@ mod tests {
         .await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| project.languages.add(language));
+        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 
         let worktree_id = project
             .update(cx, |project, cx| {
@@ -5746,16 +5748,15 @@ mod tests {
 
     #[gpui::test]
     async fn test_definition(cx: &mut gpui::TestAppContext) {
-        let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-        let language = Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -5768,9 +5769,7 @@ mod tests {
         .await;
 
         let project = Project::test(fs, cx);
-        project.update(cx, |project, _| {
-            Arc::get_mut(&mut project.languages).unwrap().add(language);
-        });
+        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 
         let (tree, _) = project
             .update(cx, |project, cx| {
@@ -6682,16 +6681,15 @@ mod tests {
     async fn test_rename(cx: &mut gpui::TestAppContext) {
         cx.foreground().forbid_parking();
 
-        let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-        let language = Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        ));
+        );
+        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -6704,9 +6702,7 @@ mod tests {
         .await;
 
         let project = Project::test(fs.clone(), cx);
-        project.update(cx, |project, _| {
-            Arc::get_mut(&mut project.languages).unwrap().add(language);
-        });
+        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 
         let (tree, _) = project
             .update(cx, |project, cx| {

crates/server/src/rpc.rs 🔗

@@ -1088,10 +1088,10 @@ mod tests {
     };
     use gpui::{executor, geometry::vector::vec2f, ModelHandle, TestAppContext, ViewHandle};
     use language::{
-        tree_sitter_rust, Diagnostic, DiagnosticEntry, Language, LanguageConfig, LanguageRegistry,
-        LanguageServerConfig, OffsetRangeExt, Point, ToLspPosition,
+        tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
+        LanguageRegistry, OffsetRangeExt, Point, ToLspPosition,
     };
-    use lsp;
+    use lsp::{self, FakeLanguageServer};
     use parking_lot::Mutex;
     use postage::barrier;
     use project::{
@@ -2039,22 +2039,20 @@ mod tests {
         cx_b: &mut TestAppContext,
     ) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2261,29 +2259,29 @@ mod tests {
         cx_b: &mut TestAppContext,
     ) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
-        let (mut language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        language_server_config.set_fake_capabilities(lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string()]),
+        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(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
+                    trigger_characters: Some(vec![".".to_string()]),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
             ..Default::default()
         });
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2463,22 +2461,20 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2563,7 +2559,7 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/root-1",
@@ -2582,18 +2578,16 @@ mod tests {
         .await;
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2705,7 +2699,7 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/root-1",
@@ -2725,18 +2719,16 @@ mod tests {
         .await;
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2967,16 +2959,16 @@ mod tests {
         .await;
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        lang_registry.add(Arc::new(Language::new(
+        let mut language = Language::new(
             LanguageConfig {
                 name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
-                language_server: language_server_config,
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        )));
+        );
+        let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3092,7 +3084,7 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/code",
@@ -3112,18 +3104,16 @@ mod tests {
         .await;
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3231,7 +3221,7 @@ mod tests {
         mut rng: StdRng,
     ) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/root",
@@ -3244,19 +3234,16 @@ mod tests {
         .await;
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3337,23 +3324,21 @@ mod tests {
         cx_b: &mut TestAppContext,
     ) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         cx_b.update(|cx| editor::init(cx));
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3573,23 +3558,21 @@ mod tests {
     #[gpui::test(iterations = 10)]
     async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         cx_a.foreground().forbid_parking();
-        let mut lang_registry = Arc::new(LanguageRegistry::test());
+        let lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
         cx_b.update(|cx| editor::init(cx));
 
         // Set up a fake language server.
-        let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: language_server_config,
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )));
+        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(Default::default());
+        lang_registry.add(Arc::new(language));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -4838,7 +4821,7 @@ mod tests {
         let rng = Arc::new(Mutex::new(rng));
 
         let guest_lang_registry = Arc::new(LanguageRegistry::test());
-        let (language_server_config, _fake_language_servers) = LanguageServerConfig::fake();
+        let host_language_registry = Arc::new(LanguageRegistry::test());
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -4852,6 +4835,7 @@ mod tests {
         let operations = Rc::new(Cell::new(0));
         let mut server = TestServer::start(cx.foreground(), cx.background()).await;
         let mut clients = Vec::new();
+        let files = Arc::new(Mutex::new(Vec::new()));
 
         let mut next_entity_id = 100000;
         let mut host_cx = TestAppContext::new(
@@ -4868,7 +4852,7 @@ mod tests {
             Project::local(
                 host.client.clone(),
                 host.user_store.clone(),
-                Arc::new(LanguageRegistry::test()),
+                host_language_registry.clone(),
                 fs.clone(),
                 cx,
             )
@@ -4891,9 +4875,138 @@ mod tests {
             .await
             .unwrap();
 
+        // Set up fake language servers.
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            None,
+        );
+        let _fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
+            name: "the-fake-language-server",
+            capabilities: lsp::LanguageServer::full_capabilities(),
+            initializer: Some(Box::new({
+                let rng = rng.clone();
+                let files = files.clone();
+                let project = host_project.downgrade();
+                move |fake_server: &mut FakeLanguageServer| {
+                    fake_server.handle_request::<lsp::request::Completion, _, _>(
+                        |_, _| async move {
+                            Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem {
+                                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+                                    range: lsp::Range::new(
+                                        lsp::Position::new(0, 0),
+                                        lsp::Position::new(0, 0),
+                                    ),
+                                    new_text: "the-new-text".to_string(),
+                                })),
+                                ..Default::default()
+                            }]))
+                        },
+                    );
+
+                    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
+                        |_, _| async move {
+                            Some(vec![lsp::CodeActionOrCommand::CodeAction(
+                                lsp::CodeAction {
+                                    title: "the-code-action".to_string(),
+                                    ..Default::default()
+                                },
+                            )])
+                        },
+                    );
+
+                    fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
+                        |params, _| async move {
+                            Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
+                                params.position,
+                                params.position,
+                            )))
+                        },
+                    );
+
+                    fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
+                        let files = files.clone();
+                        let rng = rng.clone();
+                        move |_, _| {
+                            let files = files.clone();
+                            let rng = rng.clone();
+                            async move {
+                                let files = files.lock();
+                                let mut rng = rng.lock();
+                                let count = rng.gen_range::<usize, _>(1..3);
+                                let files = (0..count)
+                                    .map(|_| files.choose(&mut *rng).unwrap())
+                                    .collect::<Vec<_>>();
+                                log::info!("LSP: Returning definitions in files {:?}", &files);
+                                Some(lsp::GotoDefinitionResponse::Array(
+                                    files
+                                        .into_iter()
+                                        .map(|file| lsp::Location {
+                                            uri: lsp::Url::from_file_path(file).unwrap(),
+                                            range: Default::default(),
+                                        })
+                                        .collect(),
+                                ))
+                            }
+                        }
+                    });
+
+                    fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
+                        let rng = rng.clone();
+                        let project = project.clone();
+                        move |params, mut cx| {
+                            let highlights = if let Some(project) = project.upgrade(&cx) {
+                                project.update(&mut cx, |project, cx| {
+                                    let path = params
+                                        .text_document_position_params
+                                        .text_document
+                                        .uri
+                                        .to_file_path()
+                                        .unwrap();
+                                    let (worktree, relative_path) =
+                                        project.find_local_worktree(&path, cx)?;
+                                    let project_path =
+                                        ProjectPath::from((worktree.read(cx).id(), relative_path));
+                                    let buffer =
+                                        project.get_open_buffer(&project_path, cx)?.read(cx);
+
+                                    let mut highlights = Vec::new();
+                                    let highlight_count = rng.lock().gen_range(1..=5);
+                                    let mut prev_end = 0;
+                                    for _ in 0..highlight_count {
+                                        let range =
+                                            buffer.random_byte_range(prev_end, &mut *rng.lock());
+                                        let start = buffer
+                                            .offset_to_point_utf16(range.start)
+                                            .to_lsp_position();
+                                        let end = buffer
+                                            .offset_to_point_utf16(range.end)
+                                            .to_lsp_position();
+                                        highlights.push(lsp::DocumentHighlight {
+                                            range: lsp::Range::new(start, end),
+                                            kind: Some(lsp::DocumentHighlightKind::READ),
+                                        });
+                                        prev_end = range.end;
+                                    }
+                                    Some(highlights)
+                                })
+                            } else {
+                                None
+                            };
+                            async move { highlights }
+                        }
+                    });
+                }
+            })),
+        });
+        host_language_registry.add(Arc::new(language));
+
         clients.push(cx.foreground().spawn(host.simulate_host(
             host_project,
-            language_server_config,
+            files,
             operations.clone(),
             max_operations,
             rng.clone(),
@@ -5324,264 +5437,128 @@ mod tests {
             })
         }
 
-        fn simulate_host(
+        async fn simulate_host(
             mut self,
             project: ModelHandle<Project>,
-            mut language_server_config: LanguageServerConfig,
+            files: Arc<Mutex<Vec<PathBuf>>>,
             operations: Rc<Cell<usize>>,
             max_operations: usize,
             rng: Arc<Mutex<StdRng>>,
             mut cx: TestAppContext,
-        ) -> impl Future<Output = (Self, TestAppContext)> {
-            let files: Arc<Mutex<Vec<PathBuf>>> = Default::default();
-
-            // Set up a fake language server.
-            language_server_config.set_fake_initializer({
-                let rng = rng.clone();
-                let files = files.clone();
-                let project = project.downgrade();
-                move |fake_server| {
-                    fake_server.handle_request::<lsp::request::Completion, _, _>(
-                        |_, _| async move {
-                            Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem {
-                                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                                    range: lsp::Range::new(
-                                        lsp::Position::new(0, 0),
-                                        lsp::Position::new(0, 0),
-                                    ),
-                                    new_text: "the-new-text".to_string(),
-                                })),
-                                ..Default::default()
-                            }]))
-                        },
-                    );
-
-                    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
-                        |_, _| async move {
-                            Some(vec![lsp::CodeActionOrCommand::CodeAction(
-                                lsp::CodeAction {
-                                    title: "the-code-action".to_string(),
-                                    ..Default::default()
-                                },
-                            )])
-                        },
-                    );
-
-                    fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
-                        |params, _| async move {
-                            Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
-                                params.position,
-                                params.position,
-                            )))
-                        },
-                    );
+        ) -> (Self, TestAppContext) {
+            let fs = project.read_with(&cx, |project, _| project.fs().clone());
+            while operations.get() < max_operations {
+                operations.set(operations.get() + 1);
 
-                    fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
-                        let files = files.clone();
-                        let rng = rng.clone();
-                        move |_, _| {
-                            let files = files.clone();
-                            let rng = rng.clone();
-                            async move {
-                                let files = files.lock();
-                                let mut rng = rng.lock();
-                                let count = rng.gen_range::<usize, _>(1..3);
-                                let files = (0..count)
-                                    .map(|_| files.choose(&mut *rng).unwrap())
-                                    .collect::<Vec<_>>();
-                                log::info!("LSP: Returning definitions in files {:?}", &files);
-                                Some(lsp::GotoDefinitionResponse::Array(
-                                    files
-                                        .into_iter()
-                                        .map(|file| lsp::Location {
-                                            uri: lsp::Url::from_file_path(file).unwrap(),
-                                            range: Default::default(),
-                                        })
-                                        .collect(),
-                                ))
+                let distribution = rng.lock().gen_range::<usize, _>(0..100);
+                match distribution {
+                    0..=20 if !files.lock().is_empty() => {
+                        let path = files.lock().choose(&mut *rng.lock()).unwrap().clone();
+                        let mut path = path.as_path();
+                        while let Some(parent_path) = path.parent() {
+                            path = parent_path;
+                            if rng.lock().gen() {
+                                break;
                             }
                         }
-                    });
 
-                    fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
-                        let rng = rng.clone();
-                        let project = project.clone();
-                        move |params, mut cx| {
-                            let highlights = if let Some(project) = project.upgrade(&cx) {
-                                project.update(&mut cx, |project, cx| {
-                                    let path = params
-                                        .text_document_position_params
-                                        .text_document
-                                        .uri
-                                        .to_file_path()
-                                        .unwrap();
-                                    let (worktree, relative_path) =
-                                        project.find_local_worktree(&path, cx)?;
-                                    let project_path =
-                                        ProjectPath::from((worktree.read(cx).id(), relative_path));
-                                    let buffer =
-                                        project.get_open_buffer(&project_path, cx)?.read(cx);
-
-                                    let mut highlights = Vec::new();
-                                    let highlight_count = rng.lock().gen_range(1..=5);
-                                    let mut prev_end = 0;
-                                    for _ in 0..highlight_count {
-                                        let range =
-                                            buffer.random_byte_range(prev_end, &mut *rng.lock());
-                                        let start = buffer
-                                            .offset_to_point_utf16(range.start)
-                                            .to_lsp_position();
-                                        let end = buffer
-                                            .offset_to_point_utf16(range.end)
-                                            .to_lsp_position();
-                                        highlights.push(lsp::DocumentHighlight {
-                                            range: lsp::Range::new(start, end),
-                                            kind: Some(lsp::DocumentHighlightKind::READ),
-                                        });
-                                        prev_end = range.end;
-                                    }
-                                    Some(highlights)
-                                })
-                            } else {
-                                None
-                            };
-                            async move { highlights }
+                        log::info!("Host: find/create local worktree {:?}", path);
+                        let find_or_create_worktree = project.update(&mut cx, |project, cx| {
+                            project.find_or_create_local_worktree(path, true, cx)
+                        });
+                        let find_or_create_worktree = async move {
+                            find_or_create_worktree.await.unwrap();
+                        };
+                        if rng.lock().gen() {
+                            cx.background().spawn(find_or_create_worktree).detach();
+                        } else {
+                            find_or_create_worktree.await;
                         }
-                    });
-                }
-            });
-
-            project.update(&mut cx, |project, _| {
-                project.languages().add(Arc::new(Language::new(
-                    LanguageConfig {
-                        name: "Rust".into(),
-                        path_suffixes: vec!["rs".to_string()],
-                        language_server: language_server_config,
-                        ..Default::default()
-                    },
-                    None,
-                )));
-            });
-
-            async move {
-                let fs = project.read_with(&cx, |project, _| project.fs().clone());
-                while operations.get() < max_operations {
-                    operations.set(operations.get() + 1);
-
-                    let distribution = rng.lock().gen_range::<usize, _>(0..100);
-                    match distribution {
-                        0..=20 if !files.lock().is_empty() => {
-                            let path = files.lock().choose(&mut *rng.lock()).unwrap().clone();
-                            let mut path = path.as_path();
-                            while let Some(parent_path) = path.parent() {
-                                path = parent_path;
-                                if rng.lock().gen() {
-                                    break;
-                                }
-                            }
+                    }
+                    10..=80 if !files.lock().is_empty() => {
+                        let buffer = if self.buffers.is_empty() || rng.lock().gen() {
+                            let file = files.lock().choose(&mut *rng.lock()).unwrap().clone();
+                            let (worktree, path) = project
+                                .update(&mut cx, |project, cx| {
+                                    project.find_or_create_local_worktree(file.clone(), true, cx)
+                                })
+                                .await
+                                .unwrap();
+                            let project_path =
+                                worktree.read_with(&cx, |worktree, _| (worktree.id(), path));
+                            log::info!(
+                                "Host: opening path {:?}, worktree {}, relative_path {:?}",
+                                file,
+                                project_path.0,
+                                project_path.1
+                            );
+                            let buffer = project
+                                .update(&mut cx, |project, cx| {
+                                    project.open_buffer(project_path, cx)
+                                })
+                                .await
+                                .unwrap();
+                            self.buffers.insert(buffer.clone());
+                            buffer
+                        } else {
+                            self.buffers
+                                .iter()
+                                .choose(&mut *rng.lock())
+                                .unwrap()
+                                .clone()
+                        };
 
-                            log::info!("Host: find/create local worktree {:?}", path);
-                            let find_or_create_worktree = project.update(&mut cx, |project, cx| {
-                                project.find_or_create_local_worktree(path, true, cx)
+                        if rng.lock().gen_bool(0.1) {
+                            cx.update(|cx| {
+                                log::info!(
+                                    "Host: dropping buffer {:?}",
+                                    buffer.read(cx).file().unwrap().full_path(cx)
+                                );
+                                self.buffers.remove(&buffer);
+                                drop(buffer);
                             });
-                            let find_or_create_worktree = async move {
-                                find_or_create_worktree.await.unwrap();
-                            };
-                            if rng.lock().gen() {
-                                cx.background().spawn(find_or_create_worktree).detach();
-                            } else {
-                                find_or_create_worktree.await;
-                            }
-                        }
-                        10..=80 if !files.lock().is_empty() => {
-                            let buffer = if self.buffers.is_empty() || rng.lock().gen() {
-                                let file = files.lock().choose(&mut *rng.lock()).unwrap().clone();
-                                let (worktree, path) = project
-                                    .update(&mut cx, |project, cx| {
-                                        project.find_or_create_local_worktree(
-                                            file.clone(),
-                                            true,
-                                            cx,
-                                        )
-                                    })
-                                    .await
-                                    .unwrap();
-                                let project_path =
-                                    worktree.read_with(&cx, |worktree, _| (worktree.id(), path));
+                        } else {
+                            buffer.update(&mut cx, |buffer, cx| {
                                 log::info!(
-                                    "Host: opening path {:?}, worktree {}, relative_path {:?}",
-                                    file,
-                                    project_path.0,
-                                    project_path.1
+                                    "Host: updating buffer {:?} ({})",
+                                    buffer.file().unwrap().full_path(cx),
+                                    buffer.remote_id()
                                 );
-                                let buffer = project
-                                    .update(&mut cx, |project, cx| {
-                                        project.open_buffer(project_path, cx)
-                                    })
-                                    .await
-                                    .unwrap();
-                                self.buffers.insert(buffer.clone());
-                                buffer
-                            } else {
-                                self.buffers
-                                    .iter()
-                                    .choose(&mut *rng.lock())
-                                    .unwrap()
-                                    .clone()
-                            };
-
-                            if rng.lock().gen_bool(0.1) {
-                                cx.update(|cx| {
-                                    log::info!(
-                                        "Host: dropping buffer {:?}",
-                                        buffer.read(cx).file().unwrap().full_path(cx)
-                                    );
-                                    self.buffers.remove(&buffer);
-                                    drop(buffer);
-                                });
-                            } else {
-                                buffer.update(&mut cx, |buffer, cx| {
-                                    log::info!(
-                                        "Host: updating buffer {:?} ({})",
-                                        buffer.file().unwrap().full_path(cx),
-                                        buffer.remote_id()
-                                    );
-                                    buffer.randomly_edit(&mut *rng.lock(), 5, cx)
-                                });
-                            }
+                                buffer.randomly_edit(&mut *rng.lock(), 5, cx)
+                            });
                         }
-                        _ => loop {
-                            let path_component_count = rng.lock().gen_range::<usize, _>(1..=5);
-                            let mut path = PathBuf::new();
-                            path.push("/");
-                            for _ in 0..path_component_count {
-                                let letter = rng.lock().gen_range(b'a'..=b'z');
-                                path.push(std::str::from_utf8(&[letter]).unwrap());
-                            }
-                            path.set_extension("rs");
-                            let parent_path = path.parent().unwrap();
-
-                            log::info!("Host: creating file {:?}", path,);
-
-                            if fs.create_dir(&parent_path).await.is_ok()
-                                && fs.create_file(&path, Default::default()).await.is_ok()
-                            {
-                                files.lock().push(path);
-                                break;
-                            } else {
-                                log::info!("Host: cannot create file");
-                            }
-                        },
                     }
+                    _ => loop {
+                        let path_component_count = rng.lock().gen_range::<usize, _>(1..=5);
+                        let mut path = PathBuf::new();
+                        path.push("/");
+                        for _ in 0..path_component_count {
+                            let letter = rng.lock().gen_range(b'a'..=b'z');
+                            path.push(std::str::from_utf8(&[letter]).unwrap());
+                        }
+                        path.set_extension("rs");
+                        let parent_path = path.parent().unwrap();
 
-                    cx.background().simulate_random_delay().await;
-                }
+                        log::info!("Host: creating file {:?}", path,);
 
-                log::info!("Host done");
+                        if fs.create_dir(&parent_path).await.is_ok()
+                            && fs.create_file(&path, Default::default()).await.is_ok()
+                        {
+                            files.lock().push(path);
+                            break;
+                        } else {
+                            log::info!("Host: cannot create file");
+                        }
+                    },
+                }
 
-                self.project = Some(project);
-                (self, cx)
+                cx.background().simulate_random_delay().await;
             }
+
+            log::info!("Host done");
+
+            self.project = Some(project);
+            (self, cx)
         }
 
         pub async fn simulate_guest(