Fix errors resulting from rebase

Isaac Clayton created

Change summary

Cargo.lock                          |  107 
crates/language/src/language.rs     |    2 
crates/plugin_runtime/Cargo.toml    |    8 
crates/project/src/lsp_command.rs   |    4 
crates/project/src/project.rs       | 3078 ------------------------------
crates/project/src/project_tests.rs |   42 
6 files changed, 139 insertions(+), 3,102 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1183,23 +1183,24 @@ dependencies = [
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fa7c3188913c2d11a361e0431e135742372a2709a99b103e79758e11a0a797e"
+checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29285f70fd396a8f64455a15a6e1d390322e4a5f5186de513141313211b0a23e"
+checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5"
 dependencies = [
  "cranelift-bforest",
  "cranelift-codegen-meta",
  "cranelift-codegen-shared",
  "cranelift-entity",
+ "cranelift-isle",
  "gimli",
  "log",
  "regalloc2",
@@ -1209,33 +1210,33 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057eac2f202ec95aebfd8d495e88560ac085f6a415b3c6c28529dc5eb116a141"
+checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75d93869efd18874a9341cfd8ad66bcb08164e86357a694a0e939d29e87410b9"
+checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69"
 
 [[package]]
 name = "cranelift-entity"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e34bd7a1fefa902c90a921b36323f17a398b788fa56a75f07a29d83b6e28808"
+checksum = "886c59a5e0de1f06dbb7da80db149c75de10d5e2caca07cdd9fef8a5918a6336"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "457018dd2d6ee300953978f63215b5edf3ae42dbdf8c7c038972f10394599f72"
+checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -1243,11 +1244,17 @@ dependencies = [
  "target-lexicon",
 ]
 
+[[package]]
+name = "cranelift-isle"
+version = "0.85.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43"
+
 [[package]]
 name = "cranelift-native"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bba027cc41bf1d0eee2ddf16caba2ee1be682d0214520fff0129d2c6557fda89"
+checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -1256,9 +1263,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.84.0"
+version = "0.85.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b17639ced10b9916c9be120d38c872ea4f9888aa09248568b10056ef0559bfa"
+checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -4181,9 +4188,9 @@ dependencies = [
 
 [[package]]
 name = "regalloc2"
-version = "0.1.3"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904196c12c9f55d3aea578613219f493ced8e05b3d0c6a42d11cb4142d8b4879"
+checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c"
 dependencies = [
  "fxhash",
  "log",
@@ -6314,9 +6321,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasi-cap-std-sync"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1029972e08194fe0ca67a83221945fff9d6d1b0dd8b752c6073b45d0254ac71b"
+checksum = "c1c4e73ed64b92ae87b416f4274b3c827180b02b67f835f66a86fc4267b77349"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -6338,9 +6345,9 @@ dependencies = [
 
 [[package]]
 name = "wasi-common"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "396cc8d920f924474f589f6a2202ad9783579ef12f96e6b675d0b2f3917c7126"
+checksum = "cc983eb93607a61f64152ec8728bf453f4dfdf22e7ab1784faac3297fe9a035e"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -6431,18 +6438,18 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.84.0"
+version = "0.85.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77dc97c22bb5ce49a47b745bed8812d30206eff5ef3af31424f2c1820c0974b2"
+checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7"
 dependencies = [
  "indexmap",
 ]
 
 [[package]]
 name = "wasmtime"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfdd1101bdfa0414a19018ec0a091951a20b695d4d04f858d49f6c4cc53cd8dd"
+checksum = "e76e2b2833bb0ece666ccdbed7b71b617d447da11f1bb61f4f2bab2648f745ee"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -6474,9 +6481,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cache"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79da81ed0724392948ad7a0fb5088ff1bd15fa937356c8c037c6b1c8b5473cde"
+checksum = "743a9f142d93318262d7e1fe329394ff2e8f86a1df45ae5e4f0eedba215ca5ce"
 dependencies = [
  "anyhow",
  "base64 0.13.0",
@@ -6494,9 +6501,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cranelift"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e78edcfb0daa9a9579ac379d00e2d5a5b2a60c0d653c8c95e8412f2166acb9"
+checksum = "5dc0f80afa1ce97083a7168e6b6948d015d6237369e9f4a511d38c9c4ac8fbb9"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -6516,9 +6523,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-environ"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4201389132ec467981980549574b33fc70d493b40f2c045c8ce5c7b54fbad97e"
+checksum = "0816d9365196f1f447060087e0f87239ccded830bd54970a1168b0c9c8e824c9"
 dependencies = [
  "anyhow",
  "cranelift-entity",
@@ -6536,9 +6543,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-fiber"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ba6777a84b44f9a384b5c9d511ae3d86534438b7e25d928b8e8e858ecad5df2"
+checksum = "715afdb87a3bcf1eae3f098c742d650fb783abdb8a7ca87076ea1cabecabea5d"
 dependencies = [
  "cc",
  "rustix",
@@ -6547,9 +6554,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1587ca7752d00862faa540d00fd28e5ccf1ac61ba19756449193f1153cb2b127"
+checksum = "5c687f33cfa0f89ec1646929d0ff102087052cf9f0d15533de56526b0da0d1b3"
 dependencies = [
  "addr2line",
  "anyhow",
@@ -6574,9 +6581,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit-debug"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b27233ab6c8934b23171c64f215f902ef19d18c1712b46a0674286d1ef28d5dd"
+checksum = "b252d1d025f94f3954ba2111f12f3a22826a0764a11c150c2d46623115a69e27"
 dependencies = [
  "lazy_static",
  "object",
@@ -6585,9 +6592,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-runtime"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47d3b0b8f13db47db59d616e498fe45295819d04a55f9921af29561827bdb816"
+checksum = "ace251693103c9facbbd7df87a29a75e68016e48bc83c09133f2fda6b575e0ab"
 dependencies = [
  "anyhow",
  "backtrace",
@@ -6612,9 +6619,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-types"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1630d9dca185299bec7f557a7e73b28742fe5590caf19df001422282a0a98ad1"
+checksum = "d129b0487a95986692af8708ffde9c50b0568dcefd79200941d475713b4f40bb"
 dependencies = [
  "cranelift-entity",
  "serde",
@@ -6624,9 +6631,9 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d507b07263a4923440da662c611affc2e671714bb6e5f68f6b068a5736bcd7f"
+checksum = "fb49791530b3a3375897a6d5a8bfa9914101ef8a672d01c951e70b46fd953c15"
 dependencies = [
  "anyhow",
  "wasi-cap-std-sync",
@@ -6751,9 +6758,9 @@ dependencies = [
 
 [[package]]
 name = "wiggle"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799e618e90d9f3d6d76943d9f2903c609b62f2ab5d16dedcff4816d38db726dd"
+checksum = "91c38020359fabec5e5ce5a3f667af72e9a203bc6fe8caeb8931d3a870754d9d"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -6766,9 +6773,9 @@ dependencies = [
 
 [[package]]
 name = "wiggle-generate"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534d70579cd9aca243e94eeb14a348dbc9ae87e7758ffebfdd4bb4a98d59b9b0"
+checksum = "adc4e4420b496b04920ae3e41424029aba95c15a5e2e2b4012d14ec83770a3ef"
 dependencies = [
  "anyhow",
  "heck 0.4.0",
@@ -6781,9 +6788,9 @@ dependencies = [
 
 [[package]]
 name = "wiggle-macro"
-version = "0.37.0"
+version = "0.38.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87216b1f58eeee44a6385de39e8c03190c209942cfa34344c9ba69426f146d8d"
+checksum = "2e541a0be1f2c4d53471d8a9df81c2d8725a3f023d8259f555c65b03d515aaab"
 dependencies = [
  "proc-macro2",
  "quote",

crates/language/src/language.rs 🔗

@@ -541,7 +541,7 @@ async fn fetch_latest_server_binary_path(
         .broadcast((language.clone(), LanguageServerBinaryStatus::Downloading))
         .await?;
     let path = adapter
-        .fetch_server_binary(version_info, http_client, container_dir.clone())
+        .fetch_server_binary(version_info, http_client, container_dir.to_path_buf())
         .await?;
     lsp_binary_statuses_tx
         .broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded))

crates/plugin_runtime/Cargo.toml 🔗

@@ -4,9 +4,9 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-wasmtime = "0.37.0"
-wasmtime-wasi = "0.37.0"
-wasi-common = "0.37.0"
+wasmtime = "0.38"
+wasmtime-wasi = "0.38"
+wasi-common = "0.38"
 anyhow = { version = "1.0", features = ["std"] }
 serde = "1.0"
 serde_json = "1.0"
@@ -15,4 +15,4 @@ pollster = "0.2.5"
 smol = "1.2.5"
 
 [build-dependencies]
-wasmtime = "0.38.1"
+wasmtime = "0.38"

crates/project/src/lsp_command.rs 🔗

@@ -389,7 +389,7 @@ impl LspCommand for GetDefinition {
                         this.open_local_buffer_via_lsp(
                             target_uri,
                             language_server.server_id(),
-                            lsp_adapter.name(),
+                            lsp_adapter.name.clone(),
                             cx,
                         )
                     })
@@ -610,7 +610,7 @@ impl LspCommand for GetReferences {
                         this.open_local_buffer_via_lsp(
                             lsp_location.uri,
                             language_server.server_id(),
-                            lsp_adapter.name(),
+                            lsp_adapter.name.clone(),
                             cx,
                         )
                     })

crates/project/src/project.rs 🔗

@@ -199,7 +199,7 @@ pub enum Event {
 pub enum LanguageServerState {
     Starting(Task<Option<Arc<LanguageServer>>>),
     Running {
-        adapter: Arc<dyn LspAdapter>,
+        adapter: Arc<LspAdapter>,
         server: Arc<LanguageServer>,
     },
 }
@@ -708,7 +708,7 @@ impl Project {
         })
     }
 
-    fn on_settings_changed(&mut self, cx: &mut ModelContext<'_, Self>) {
+    fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
         let settings = cx.global::<Settings>();
 
         let mut language_servers_to_start = Vec::new();
@@ -734,7 +734,7 @@ impl Project {
             if let Some(lsp_adapter) = language.lsp_adapter() {
                 if !settings.enable_language_server(Some(&language.name())) {
                     let lsp_name = &lsp_adapter.name;
-                    for (worktree_id, started_lsp_name) in self.language_servers.keys() {
+                    for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
                         if lsp_name == started_lsp_name {
                             language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
                         }
@@ -1627,6 +1627,7 @@ impl Project {
         })
     }
 
+    /// LanguageServerName is owned, because it is inserted into a map
     fn open_local_buffer_via_lsp(
         &mut self,
         abs_path: lsp::Url,
@@ -1648,10 +1649,11 @@ impl Project {
                         this.create_local_worktree(&abs_path, false, cx)
                     })
                     .await?;
-                let name = lsp_adapter.name.clone();
                 this.update(&mut cx, |this, cx| {
-                    this.language_servers
-                        .insert((worktree.read(cx).id(), name), (lsp_adapter, lsp_server));
+                    this.language_server_ids.insert(
+                        (worktree.read(cx).id(), language_server_name),
+                        language_server_id,
+                    );
                 });
                 (worktree, PathBuf::new())
             };
@@ -2002,9 +2004,10 @@ impl Project {
     fn language_servers_for_worktree(
         &self,
         worktree_id: WorktreeId,
-    ) -> impl Iterator<Item = &(Arc<LspAdapter>, Arc<LanguageServer>)> {
-        self.language_servers.iter().filter_map(
-            move |((language_server_worktree_id, _), server)| {
+    ) -> impl Iterator<Item = (&Arc<LspAdapter>, &Arc<LanguageServer>)> {
+        self.language_server_ids
+            .iter()
+            .filter_map(move |((language_server_worktree_id, _), id)| {
                 if *language_server_worktree_id == worktree_id {
                     if let Some(LanguageServerState::Running { adapter, server }) =
                         self.language_servers.get(&id)
@@ -2042,7 +2045,7 @@ impl Project {
         worktree_id: WorktreeId,
         worktree_path: Arc<Path>,
         language: Arc<Language>,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut ModelContext<Self>,
     ) {
         if !cx
             .global::<Settings>()
@@ -2056,8 +2059,8 @@ impl Project {
         } else {
             return;
         };
+        let key = (worktree_id, adapter.name.clone());
 
-        let key = (worktree_id, adapter.name);
         self.language_server_ids
             .entry(key.clone())
             .or_insert_with(|| {
@@ -2069,18 +2072,15 @@ impl Project {
                     self.client.http_client(),
                     cx,
                 );
-
                 self.language_servers.insert(
                     server_id,
                     LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
                         let language_server = language_server?.await.log_err()?;
                         let language_server = language_server
-                            .initialize(adapter.initialization_options())
+                            .initialize(adapter.initialization_options.clone())
                             .await
                             .log_err()?;
                         let this = this.upgrade(&cx)?;
-                        let disk_based_diagnostics_progress_token =
-                            adapter.disk_based_diagnostics_progress_token();
 
                         language_server
                             .on_notification::<lsp::notification::PublishDiagnostics, _>({
@@ -2089,10 +2089,9 @@ impl Project {
                                 move |params, mut cx| {
                                     if let Some(this) = this.upgrade(&cx) {
                                         this.update(&mut cx, |this, cx| {
-                                            // TODO(isaac): remove block on
-                                            smol::block_on(this.on_lsp_diagnostics_published(
+                                            this.on_lsp_diagnostics_published(
                                                 server_id, params, &adapter, cx,
-                                            ))
+                                            );
                                         });
                                     }
                                 }
@@ -2170,11 +2169,13 @@ impl Project {
                                         language_server.clone(),
                                         cx,
                                     )
-
                                 }
                             })
                             .detach();
 
+                        let disk_based_diagnostics_progress_token =
+                            adapter.disk_based_diagnostics_progress_token.clone();
+
                         language_server
                             .on_notification::<lsp::notification::Progress, _>({
                                 let this = this.downgrade();
@@ -2184,7 +2185,7 @@ impl Project {
                                             this.on_lsp_progress(
                                                 params,
                                                 server_id,
-                                                disk_based_diagnostics_progress_token,
+                                                disk_based_diagnostics_progress_token.clone(),
                                                 cx,
                                             );
                                         });
@@ -2258,7 +2259,7 @@ impl Project {
                                         continue;
                                     };
                                     if file.worktree.read(cx).id() != key.0
-                                        || language.lsp_adapter().map(|a| a.name())
+                                        || language.lsp_adapter().map(|a| a.name.clone())
                                             != Some(key.1.clone())
                                     {
                                         continue;
@@ -2271,14 +2272,15 @@ impl Project {
                                         .or_insert_with(|| vec![(0, buffer.text_snapshot())]);
                                     let (version, initial_snapshot) = versions.last().unwrap();
                                     let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
-                                    let language_id =
-                                        adapter.id_for_language(language.name().as_ref());
                                     language_server
                                         .notify::<lsp::notification::DidOpenTextDocument>(
                                             lsp::DidOpenTextDocumentParams {
                                                 text_document: lsp::TextDocumentItem::new(
                                                     uri,
-                                                    language_id.unwrap_or_default(),
+                                                    adapter
+                                                        .id_for_language
+                                                        .clone()
+                                                        .unwrap_or_default(),
                                                     *version,
                                                     initial_snapshot.text(),
                                                 ),
@@ -2306,6 +2308,8 @@ impl Project {
                         })
                     })),
                 );
+
+                server_id
             });
     }
 
@@ -2394,7 +2398,7 @@ impl Project {
         worktree_id: WorktreeId,
         fallback_path: Arc<Path>,
         language: Arc<Language>,
-        cx: &mut ModelContext<'_, Self>,
+        cx: &mut ModelContext<Self>,
     ) {
         let adapter = if let Some(adapter) = language.lsp_adapter() {
             adapter
@@ -2402,7 +2406,8 @@ impl Project {
             return;
         };
 
-        let stop = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
+        let server_name = adapter.name.clone();
+        let stop = self.stop_language_server(worktree_id, server_name.clone(), cx);
         cx.spawn_weak(|this, mut cx| async move {
             let (original_root_path, orphaned_worktrees) = stop.await;
             if let Some(this) = this.upgrade(&cx) {
@@ -2455,7 +2460,7 @@ impl Project {
         &mut self,
         progress: lsp::ProgressParams,
         server_id: usize,
-        disk_based_diagnostics_progress_token: Option<&str>,
+        disk_based_diagnostics_progress_token: Option<String>,
         cx: &mut ModelContext<Self>,
     ) {
         let token = match progress.token {
@@ -2479,7 +2484,8 @@ impl Project {
             return;
         }
 
-        let same_token = Some(token.as_ref()) == disk_based_diagnostics_progress_token;
+        let same_token =
+            Some(token.as_ref()) == disk_based_diagnostics_progress_token.as_ref().map(|x| &**x);
 
         match progress {
             lsp::WorkDoneProgress::Begin(report) => {
@@ -3533,50 +3539,17 @@ impl Project {
                         {
                             continue;
                         }
-                            let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref()
-                            {
-                                // If the language server provides a range to overwrite, then
-                                // check that the range is valid.
-                                Some(lsp::CompletionTextEdit::Edit(edit)) => {
-                                    let range = range_from_lsp(edit.range);
-                                    let start = snapshot.clip_point_utf16(range.start, Bias::Left);
-                                    let end = snapshot.clip_point_utf16(range.end, Bias::Left);
-                                    if start != range.start || end != range.end {
-                                        log::info!("completion out of expected range");
-                                        return None;
-                                    }
-                                    (
-                                        snapshot.anchor_before(start)..snapshot.anchor_after(end),
-                                        edit.new_text.clone(),
-                                    )
-                                }
-                                // If the language server does not provide a range, then infer
-                                // the range based on the syntax tree.
-                                None => {
-                                    if position != clipped_position {
-                                        log::info!("completion out of expected range");
-                                        return None;
-                                    }
-                                    let Range { start, end } = range_for_token
-                                        .get_or_insert_with(|| {
-                                            let offset = position.to_offset(&snapshot);
-                                            let (range, kind) = snapshot.surrounding_word(offset);
-                                            if kind == Some(CharKind::Word) {
-                                                range
-                                            } else {
-                                                offset..offset
-                                            }
-                                        })
-                                        .clone();
-                                    let text = lsp_completion
-                                        .insert_text
-                                        .as_ref()
-                                        .unwrap_or(&lsp_completion.label)
-                                        .clone();
-                                    (
-                                        snapshot.anchor_before(start)..snapshot.anchor_after(end),
-                                        text.clone(),
-                                    )
+
+                        let (old_range, new_text) = match lsp_completion.text_edit.as_ref() {
+                            // If the language server provides a range to overwrite, then
+                            // check that the range is valid.
+                            Some(lsp::CompletionTextEdit::Edit(edit)) => {
+                                let range = range_from_lsp(edit.range);
+                                let start = snapshot.clip_point_utf16(range.start, Bias::Left);
+                                let end = snapshot.clip_point_utf16(range.end, Bias::Left);
+                                if start != range.start || end != range.end {
+                                    log::info!("completion out of expected range");
+                                    continue;
                                 }
                                 (
                                     snapshot.anchor_before(start)..snapshot.anchor_after(end),
@@ -3590,22 +3563,6 @@ impl Project {
                                     log::info!("completion out of expected range");
                                     continue;
                                 }
-                            };
-
-                            LineEnding::normalize(&mut new_text);
-                            Some(Completion {
-                                old_range,
-                                new_text,
-                                label: {
-                                    match language.as_ref() {
-                                        Some(l) => l.label_for_completion(&lsp_completion).await,
-                                        None => None,
-                                    }
-                                    .unwrap_or_else(|| {
-                                        CodeLabel::plain(
-                                            lsp_completion.label.clone(),
-                                            lsp_completion.filter_text.as_deref(),
-                                        )
                                 let Range { start, end } = range_for_token
                                     .get_or_insert_with(|| {
                                         let offset = position.to_offset(&snapshot);
@@ -3690,13 +3647,14 @@ impl Project {
                     })
                     .await;
 
-                let mut completions = Vec::new();
+                let mut result = Vec::new();
                 for completion in response.completions.into_iter() {
-                    completions.push(
-                        language::proto::deserialize_completion(completion, language.clone()).await,
-                    );
+                    let completion =
+                        language::proto::deserialize_completion(completion, language.clone())
+                            .await?;
+                    result.push(completion);
                 }
-                completions.into_iter().collect()
+                Ok(result)
             })
         } else {
             Task::ready(Ok(Default::default()))
@@ -4066,7 +4024,7 @@ impl Project {
                             this.open_local_buffer_via_lsp(
                                 op.text_document.uri,
                                 language_server.server_id(),
-                                lsp_adapter.name(),
+                                lsp_adapter.name.clone(),
                                 cx,
                             )
                         })
@@ -5998,11 +5956,11 @@ impl Project {
         &self,
         buffer: &Buffer,
         cx: &AppContext,
-    ) -> Option<&(Arc<LspAdapter>, Arc<LanguageServer>)> {
+    ) -> Option<(&Arc<LspAdapter>, &Arc<LanguageServer>)> {
         if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
             let name = language.lsp_adapter()?.name.clone();
             let worktree_id = file.worktree_id(cx);
-            let key = (worktree_id, language.lsp_adapter()?.name());
+            let key = (worktree_id, name);
 
             if let Some(server_id) = self.language_server_ids.get(&key) {
                 if let Some(LanguageServerState::Running { adapter, server }) =
@@ -6302,2931 +6260,3 @@ impl Item for Buffer {
         File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::worktree::WorktreeHandle;
-
-    use super::{Event, *};
-    use fs::RealFs;
-    use futures::{future, StreamExt};
-    use gpui::{executor::Deterministic, test::subscribe};
-    use language::{
-        tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
-        OffsetRangeExt, Point, ToPoint,
-    };
-    use lsp::Url;
-    use serde_json::json;
-    use std::{cell::RefCell, os::unix, path::PathBuf, rc::Rc, task::Poll};
-    use unindent::Unindent as _;
-    use util::{assert_set_eq, test::temp_tree};
-
-    #[gpui::test]
-    async fn test_populate_and_search(cx: &mut gpui::TestAppContext) {
-        let dir = temp_tree(json!({
-            "root": {
-                "apple": "",
-                "banana": {
-                    "carrot": {
-                        "date": "",
-                        "endive": "",
-                    }
-                },
-                "fennel": {
-                    "grape": "",
-                }
-            }
-        }));
-
-        let root_link_path = dir.path().join("root_link");
-        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
-        unix::fs::symlink(
-            &dir.path().join("root/fennel"),
-            &dir.path().join("root/finnochio"),
-        )
-        .unwrap();
-
-        let project = Project::test(Arc::new(RealFs), [root_link_path.as_ref()], cx).await;
-
-        project.read_with(cx, |project, cx| {
-            let tree = project.worktrees(cx).next().unwrap().read(cx);
-            assert_eq!(tree.file_count(), 5);
-            assert_eq!(
-                tree.inode_for_path("fennel/grape"),
-                tree.inode_for_path("finnochio/grape")
-            );
-        });
-
-        let cancel_flag = Default::default();
-        let results = project
-            .read_with(cx, |project, cx| {
-                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
-            })
-            .await;
-        assert_eq!(
-            results
-                .into_iter()
-                .map(|result| result.path)
-                .collect::<Vec<Arc<Path>>>(),
-            vec![
-                PathBuf::from("banana/carrot/date").into(),
-                PathBuf::from("banana/carrot/endive").into(),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let mut rust_language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut json_language = Language::new(
-            LanguageConfig {
-                name: "JSON".into(),
-                path_suffixes: vec!["json".to_string()],
-                ..Default::default()
-            },
-            None,
-        );
-        let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(Arc::new(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(Arc::new(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(
-            "/the-root",
-            json!({
-                "test.rs": "const A: i32 = 1;",
-                "test2.rs": "",
-                "Cargo.toml": "a = 1",
-                "package.json": "{\"a\": 1}",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages.add(Arc::new(rust_language));
-            project.languages.add(Arc::new(json_language));
-        });
-
-        // Open a buffer without an associated language server.
-        let toml_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/the-root/Cargo.toml", cx)
-            })
-            .await
-            .unwrap();
-
-        // Open a buffer with an associated language server.
-        let rust_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/the-root/test.rs", cx)
-            })
-            .await
-            .unwrap();
-
-        // A server is started up, and it is notified about Rust files.
-        let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentItem {
-                uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
-                version: 0,
-                text: "const A: i32 = 1;".to_string(),
-                language_id: Default::default()
-            }
-        );
-
-        // The buffer is configured based on the language server's capabilities.
-        rust_buffer.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer.completion_triggers(),
-                &[".".to_string(), "::".to_string()]
-            );
-        });
-        toml_buffer.read_with(cx, |buffer, _| {
-            assert!(buffer.completion_triggers().is_empty());
-        });
-
-        // Edit a buffer. The changes are reported to the language server.
-        rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], cx));
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidChangeTextDocument>()
-                .await
-                .text_document,
-            lsp::VersionedTextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
-                1
-            )
-        );
-
-        // Open a third buffer with a different associated language server.
-        let json_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/the-root/package.json", cx)
-            })
-            .await
-            .unwrap();
-
-        // A json language server is started up and is only notified about the json buffer.
-        let mut fake_json_server = fake_json_servers.next().await.unwrap();
-        assert_eq!(
-            fake_json_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentItem {
-                uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
-                version: 0,
-                text: "{\"a\": 1}".to_string(),
-                language_id: Default::default()
-            }
-        );
-
-        // This buffer is configured based on the second language server's
-        // capabilities.
-        json_buffer.read_with(cx, |buffer, _| {
-            assert_eq!(buffer.completion_triggers(), &[":".to_string()]);
-        });
-
-        // When opening another buffer whose language server is already running,
-        // it is also configured based on the existing language server's capabilities.
-        let rust_buffer2 = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/the-root/test2.rs", cx)
-            })
-            .await
-            .unwrap();
-        rust_buffer2.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer.completion_triggers(),
-                &[".".to_string(), "::".to_string()]
-            );
-        });
-
-        // Changes are reported only to servers matching the buffer's language.
-        toml_buffer.update(cx, |buffer, cx| buffer.edit([(5..5, "23")], cx));
-        rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "let x = 1;")], cx));
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidChangeTextDocument>()
-                .await
-                .text_document,
-            lsp::VersionedTextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/test2.rs").unwrap(),
-                1
-            )
-        );
-
-        // Save notifications are reported to all servers.
-        toml_buffer
-            .update(cx, |buffer, cx| buffer.save(cx))
-            .await
-            .unwrap();
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidSaveTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()
-            )
-        );
-        assert_eq!(
-            fake_json_server
-                .receive_notification::<lsp::notification::DidSaveTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()
-            )
-        );
-
-        // Renames are reported only to servers matching the buffer's language.
-        fs.rename(
-            Path::new("/the-root/test2.rs"),
-            Path::new("/the-root/test3.rs"),
-            Default::default(),
-        )
-        .await
-        .unwrap();
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/test2.rs").unwrap()
-            ),
-        );
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentItem {
-                uri: lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),
-                version: 0,
-                text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
-                language_id: Default::default()
-            },
-        );
-
-        rust_buffer2.update(cx, |buffer, cx| {
-            buffer.update_diagnostics(
-                DiagnosticSet::from_sorted_entries(
-                    vec![DiagnosticEntry {
-                        diagnostic: Default::default(),
-                        range: Anchor::MIN..Anchor::MAX,
-                    }],
-                    &buffer.snapshot(),
-                ),
-                cx,
-            );
-            assert_eq!(
-                buffer
-                    .snapshot()
-                    .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
-                    .count(),
-                1
-            );
-        });
-
-        // When the rename changes the extension of the file, the buffer gets closed on the old
-        // language server and gets opened on the new one.
-        fs.rename(
-            Path::new("/the-root/test3.rs"),
-            Path::new("/the-root/test3.json"),
-            Default::default(),
-        )
-        .await
-        .unwrap();
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),
-            ),
-        );
-        assert_eq!(
-            fake_json_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentItem {
-                uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
-                version: 0,
-                text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
-                language_id: Default::default()
-            },
-        );
-
-        // We clear the diagnostics, since the language has changed.
-        rust_buffer2.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer
-                    .snapshot()
-                    .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
-                    .count(),
-                0
-            );
-        });
-
-        // The renamed file's version resets after changing language server.
-        rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "// ")], cx));
-        assert_eq!(
-            fake_json_server
-                .receive_notification::<lsp::notification::DidChangeTextDocument>()
-                .await
-                .text_document,
-            lsp::VersionedTextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
-                1
-            )
-        );
-
-        // Restart language servers
-        project.update(cx, |project, cx| {
-            project.restart_language_servers_for_buffers(
-                vec![rust_buffer.clone(), json_buffer.clone()],
-                cx,
-            );
-        });
-
-        let mut rust_shutdown_requests = fake_rust_server
-            .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
-        let mut json_shutdown_requests = fake_json_server
-            .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
-        futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next());
-
-        let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
-        let mut fake_json_server = fake_json_servers.next().await.unwrap();
-
-        // Ensure rust document is reopened in new rust language server
-        assert_eq!(
-            fake_rust_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document,
-            lsp::TextDocumentItem {
-                uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
-                version: 1,
-                text: rust_buffer.read_with(cx, |buffer, _| buffer.text()),
-                language_id: Default::default()
-            }
-        );
-
-        // Ensure json documents are reopened in new json language server
-        assert_set_eq!(
-            [
-                fake_json_server
-                    .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                    .await
-                    .text_document,
-                fake_json_server
-                    .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                    .await
-                    .text_document,
-            ],
-            [
-                lsp::TextDocumentItem {
-                    uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
-                    version: 0,
-                    text: json_buffer.read_with(cx, |buffer, _| buffer.text()),
-                    language_id: Default::default()
-                },
-                lsp::TextDocumentItem {
-                    uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
-                    version: 1,
-                    text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
-                    language_id: Default::default()
-                }
-            ]
-        );
-
-        // Close notifications are reported only to servers matching the buffer's language.
-        cx.update(|_| drop(json_buffer));
-        let close_message = lsp::DidCloseTextDocumentParams {
-            text_document: lsp::TextDocumentIdentifier::new(
-                lsp::Url::from_file_path("/the-root/package.json").unwrap(),
-            ),
-        };
-        assert_eq!(
-            fake_json_server
-                .receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await,
-            close_message,
-        );
-    }
-
-    #[gpui::test]
-    async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": "let a = 1;",
-                "b.rs": "let b = 2;"
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir/a.rs".as_ref(), "/dir/b.rs".as_ref()], cx).await;
-
-        let buffer_a = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-        let buffer_b = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
-            .await
-            .unwrap();
-
-        project.update(cx, |project, cx| {
-            project
-                .update_diagnostics(
-                    0,
-                    lsp::PublishDiagnosticsParams {
-                        uri: Url::from_file_path("/dir/a.rs").unwrap(),
-                        version: None,
-                        diagnostics: vec![lsp::Diagnostic {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 4),
-                                lsp::Position::new(0, 5),
-                            ),
-                            severity: Some(lsp::DiagnosticSeverity::ERROR),
-                            message: "error 1".to_string(),
-                            ..Default::default()
-                        }],
-                    },
-                    &[],
-                    cx,
-                )
-                .unwrap();
-            project
-                .update_diagnostics(
-                    0,
-                    lsp::PublishDiagnosticsParams {
-                        uri: Url::from_file_path("/dir/b.rs").unwrap(),
-                        version: None,
-                        diagnostics: vec![lsp::Diagnostic {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 4),
-                                lsp::Position::new(0, 5),
-                            ),
-                            severity: Some(lsp::DiagnosticSeverity::WARNING),
-                            message: "error 2".to_string(),
-                            ..Default::default()
-                        }],
-                    },
-                    &[],
-                    cx,
-                )
-                .unwrap();
-        });
-
-        buffer_a.read_with(cx, |buffer, _| {
-            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
-            assert_eq!(
-                chunks
-                    .iter()
-                    .map(|(s, d)| (s.as_str(), *d))
-                    .collect::<Vec<_>>(),
-                &[
-                    ("let ", None),
-                    ("a", Some(DiagnosticSeverity::ERROR)),
-                    (" = 1;", None),
-                ]
-            );
-        });
-        buffer_b.read_with(cx, |buffer, _| {
-            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
-            assert_eq!(
-                chunks
-                    .iter()
-                    .map(|(s, d)| (s.as_str(), *d))
-                    .collect::<Vec<_>>(),
-                &[
-                    ("let ", None),
-                    ("b", Some(DiagnosticSeverity::WARNING)),
-                    (" = 2;", None),
-                ]
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let progress_token = "the-progress-token";
-        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_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            disk_based_diagnostics_progress_token: Some(progress_token.into()),
-            disk_based_diagnostics_sources: vec!["disk".into()],
-            ..Default::default()
-        }));
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": "fn a() { A }",
-                "b.rs": "const y: i32 = 1",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-        let worktree_id =
-            project.read_with(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
-
-        // Cause worktree to start the fake language server
-        let _buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
-            .await
-            .unwrap();
-
-        let mut events = subscribe(&project, cx);
-
-        let fake_server = fake_servers.next().await.unwrap();
-        fake_server.start_progress(progress_token).await;
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiskBasedDiagnosticsStarted {
-                language_server_id: 0,
-            }
-        );
-
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: Url::from_file_path("/dir/a.rs").unwrap(),
-                version: None,
-                diagnostics: vec![lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
-                    severity: Some(lsp::DiagnosticSeverity::ERROR),
-                    message: "undefined variable 'A'".to_string(),
-                    ..Default::default()
-                }],
-            },
-        );
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiagnosticsUpdated {
-                language_server_id: 0,
-                path: (worktree_id, Path::new("a.rs")).into()
-            }
-        );
-
-        fake_server.end_progress(progress_token);
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiskBasedDiagnosticsFinished {
-                language_server_id: 0
-            }
-        );
-
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        buffer.read_with(cx, |buffer, _| {
-            let snapshot = buffer.snapshot();
-            let diagnostics = snapshot
-                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
-                .collect::<Vec<_>>();
-            assert_eq!(
-                diagnostics,
-                &[DiagnosticEntry {
-                    range: Point::new(0, 9)..Point::new(0, 10),
-                    diagnostic: Diagnostic {
-                        severity: lsp::DiagnosticSeverity::ERROR,
-                        message: "undefined variable 'A'".to_string(),
-                        group_id: 0,
-                        is_primary: true,
-                        ..Default::default()
-                    }
-                }]
-            )
-        });
-
-        // Ensure publishing empty diagnostics twice only results in one update event.
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: Url::from_file_path("/dir/a.rs").unwrap(),
-                version: None,
-                diagnostics: Default::default(),
-            },
-        );
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiagnosticsUpdated {
-                language_server_id: 0,
-                path: (worktree_id, Path::new("a.rs")).into()
-            }
-        );
-
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: Url::from_file_path("/dir/a.rs").unwrap(),
-                version: None,
-                diagnostics: Default::default(),
-            },
-        );
-        cx.foreground().run_until_parked();
-        assert_eq!(futures::poll!(events.next()), Poll::Pending);
-    }
-
-    #[gpui::test]
-    async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let progress_token = "the-progress-token";
-        let mut language = Language::new(
-            LanguageConfig {
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            None,
-        );
-        let mut fake_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            disk_based_diagnostics_sources: vec!["disk".into()],
-            disk_based_diagnostics_progress_token: Some(progress_token.into()),
-            ..Default::default()
-        }));
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        // Simulate diagnostics starting to update.
-        let fake_server = fake_servers.next().await.unwrap();
-        fake_server.start_progress(progress_token).await;
-
-        // Restart the server before the diagnostics finish updating.
-        project.update(cx, |project, cx| {
-            project.restart_language_servers_for_buffers([buffer], cx);
-        });
-        let mut events = subscribe(&project, cx);
-
-        // Simulate the newly started server sending more diagnostics.
-        let fake_server = fake_servers.next().await.unwrap();
-        fake_server.start_progress(progress_token).await;
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiskBasedDiagnosticsStarted {
-                language_server_id: 1
-            }
-        );
-        project.read_with(cx, |project, _| {
-            assert_eq!(
-                project
-                    .language_servers_running_disk_based_diagnostics()
-                    .collect::<Vec<_>>(),
-                [1]
-            );
-        });
-
-        // All diagnostics are considered done, despite the old server's diagnostic
-        // task never completing.
-        fake_server.end_progress(progress_token);
-        assert_eq!(
-            events.next().await.unwrap(),
-            Event::DiskBasedDiagnosticsFinished {
-                language_server_id: 1
-            }
-        );
-        project.read_with(cx, |project, _| {
-            assert_eq!(
-                project
-                    .language_servers_running_disk_based_diagnostics()
-                    .collect::<Vec<_>>(),
-                [0; 0]
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_toggling_enable_language_server(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
-        deterministic.forbid_parking();
-
-        let mut rust = Language::new(
-            LanguageConfig {
-                name: Arc::from("Rust"),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            None,
-        );
-        let mut fake_rust_servers = rust.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            name: "rust-lsp",
-            ..Default::default()
-        }));
-        let mut js = Language::new(
-            LanguageConfig {
-                name: Arc::from("JavaScript"),
-                path_suffixes: vec!["js".to_string()],
-                ..Default::default()
-            },
-            None,
-        );
-        let mut fake_js_servers = js.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            name: "js-lsp",
-            ..Default::default()
-        }));
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
-            .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages.add(Arc::new(rust));
-            project.languages.add(Arc::new(js));
-        });
-
-        let _rs_buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-        let _js_buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/b.js", cx))
-            .await
-            .unwrap();
-
-        let mut fake_rust_server_1 = fake_rust_servers.next().await.unwrap();
-        assert_eq!(
-            fake_rust_server_1
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document
-                .uri
-                .as_str(),
-            "file:///dir/a.rs"
-        );
-
-        let mut fake_js_server = fake_js_servers.next().await.unwrap();
-        assert_eq!(
-            fake_js_server
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document
-                .uri
-                .as_str(),
-            "file:///dir/b.js"
-        );
-
-        // Disable Rust language server, ensuring only that server gets stopped.
-        cx.update(|cx| {
-            cx.update_global(|settings: &mut Settings, _| {
-                settings.language_overrides.insert(
-                    Arc::from("Rust"),
-                    settings::LanguageSettings {
-                        enable_language_server: Some(false),
-                        ..Default::default()
-                    },
-                );
-            })
-        });
-        fake_rust_server_1
-            .receive_notification::<lsp::notification::Exit>()
-            .await;
-
-        // Enable Rust and disable JavaScript language servers, ensuring that the
-        // former gets started again and that the latter stops.
-        cx.update(|cx| {
-            cx.update_global(|settings: &mut Settings, _| {
-                settings.language_overrides.insert(
-                    Arc::from("Rust"),
-                    settings::LanguageSettings {
-                        enable_language_server: Some(true),
-                        ..Default::default()
-                    },
-                );
-                settings.language_overrides.insert(
-                    Arc::from("JavaScript"),
-                    settings::LanguageSettings {
-                        enable_language_server: Some(false),
-                        ..Default::default()
-                    },
-                );
-            })
-        });
-        let mut fake_rust_server_2 = fake_rust_servers.next().await.unwrap();
-        assert_eq!(
-            fake_rust_server_2
-                .receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await
-                .text_document
-                .uri
-                .as_str(),
-            "file:///dir/a.rs"
-        );
-        fake_js_server
-            .receive_notification::<lsp::notification::Exit>()
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        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_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            disk_based_diagnostics_sources: vec!["disk".into()],
-            ..Default::default()
-        }));
-
-        let text = "
-            fn a() { A }
-            fn b() { BB }
-            fn c() { CCC }
-        "
-        .unindent();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree("/dir", json!({ "a.rs": text })).await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        let mut fake_server = fake_servers.next().await.unwrap();
-        let open_notification = fake_server
-            .receive_notification::<lsp::notification::DidOpenTextDocument>()
-            .await;
-
-        // Edit the buffer, moving the content down
-        buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "\n\n")], cx));
-        let change_notification_1 = fake_server
-            .receive_notification::<lsp::notification::DidChangeTextDocument>()
-            .await;
-        assert!(
-            change_notification_1.text_document.version > open_notification.text_document.version
-        );
-
-        // Report some diagnostics for the initial version of the buffer
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
-                version: Some(open_notification.text_document.version),
-                diagnostics: vec![
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
-                        severity: Some(DiagnosticSeverity::ERROR),
-                        message: "undefined variable 'A'".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
-                        severity: Some(DiagnosticSeverity::ERROR),
-                        message: "undefined variable 'BB'".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(2, 9), lsp::Position::new(2, 12)),
-                        severity: Some(DiagnosticSeverity::ERROR),
-                        source: Some("disk".to_string()),
-                        message: "undefined variable 'CCC'".to_string(),
-                        ..Default::default()
-                    },
-                ],
-            },
-        );
-
-        // The diagnostics have moved down since they were created.
-        buffer.next_notification(cx).await;
-        buffer.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer
-                    .snapshot()
-                    .diagnostics_in_range::<_, Point>(Point::new(3, 0)..Point::new(5, 0), false)
-                    .collect::<Vec<_>>(),
-                &[
-                    DiagnosticEntry {
-                        range: Point::new(3, 9)..Point::new(3, 11),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::ERROR,
-                            message: "undefined variable 'BB'".to_string(),
-                            is_disk_based: true,
-                            group_id: 1,
-                            is_primary: true,
-                            ..Default::default()
-                        },
-                    },
-                    DiagnosticEntry {
-                        range: Point::new(4, 9)..Point::new(4, 12),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::ERROR,
-                            message: "undefined variable 'CCC'".to_string(),
-                            is_disk_based: true,
-                            group_id: 2,
-                            is_primary: true,
-                            ..Default::default()
-                        }
-                    }
-                ]
-            );
-            assert_eq!(
-                chunks_with_diagnostics(buffer, 0..buffer.len()),
-                [
-                    ("\n\nfn a() { ".to_string(), None),
-                    ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
-                    (" }\nfn b() { ".to_string(), None),
-                    ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
-                    (" }\nfn c() { ".to_string(), None),
-                    ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
-                    (" }\n".to_string(), None),
-                ]
-            );
-            assert_eq!(
-                chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
-                [
-                    ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
-                    (" }\nfn c() { ".to_string(), None),
-                    ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
-                ]
-            );
-        });
-
-        // Ensure overlapping diagnostics are highlighted correctly.
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
-                version: Some(open_notification.text_document.version),
-                diagnostics: vec![
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
-                        severity: Some(DiagnosticSeverity::ERROR),
-                        message: "undefined variable 'A'".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
-                        severity: Some(DiagnosticSeverity::WARNING),
-                        message: "unreachable statement".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                ],
-            },
-        );
-
-        buffer.next_notification(cx).await;
-        buffer.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer
-                    .snapshot()
-                    .diagnostics_in_range::<_, Point>(Point::new(2, 0)..Point::new(3, 0), false)
-                    .collect::<Vec<_>>(),
-                &[
-                    DiagnosticEntry {
-                        range: Point::new(2, 9)..Point::new(2, 12),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::WARNING,
-                            message: "unreachable statement".to_string(),
-                            is_disk_based: true,
-                            group_id: 4,
-                            is_primary: true,
-                            ..Default::default()
-                        }
-                    },
-                    DiagnosticEntry {
-                        range: Point::new(2, 9)..Point::new(2, 10),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::ERROR,
-                            message: "undefined variable 'A'".to_string(),
-                            is_disk_based: true,
-                            group_id: 3,
-                            is_primary: true,
-                            ..Default::default()
-                        },
-                    }
-                ]
-            );
-            assert_eq!(
-                chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
-                [
-                    ("fn a() { ".to_string(), None),
-                    ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
-                    (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
-                    ("\n".to_string(), None),
-                ]
-            );
-            assert_eq!(
-                chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
-                [
-                    (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
-                    ("\n".to_string(), None),
-                ]
-            );
-        });
-
-        // Keep editing the buffer and ensure disk-based diagnostics get translated according to the
-        // changes since the last save.
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "    ")], cx);
-            buffer.edit([(Point::new(2, 8)..Point::new(2, 10), "(x: usize)")], cx);
-            buffer.edit([(Point::new(3, 10)..Point::new(3, 10), "xxx")], cx);
-        });
-        let change_notification_2 = fake_server
-            .receive_notification::<lsp::notification::DidChangeTextDocument>()
-            .await;
-        assert!(
-            change_notification_2.text_document.version
-                > change_notification_1.text_document.version
-        );
-
-        // Handle out-of-order diagnostics
-        fake_server.notify::<lsp::notification::PublishDiagnostics>(
-            lsp::PublishDiagnosticsParams {
-                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
-                version: Some(change_notification_2.text_document.version),
-                diagnostics: vec![
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
-                        severity: Some(DiagnosticSeverity::ERROR),
-                        message: "undefined variable 'BB'".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                    lsp::Diagnostic {
-                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
-                        severity: Some(DiagnosticSeverity::WARNING),
-                        message: "undefined variable 'A'".to_string(),
-                        source: Some("disk".to_string()),
-                        ..Default::default()
-                    },
-                ],
-            },
-        );
-
-        buffer.next_notification(cx).await;
-        buffer.read_with(cx, |buffer, _| {
-            assert_eq!(
-                buffer
-                    .snapshot()
-                    .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
-                    .collect::<Vec<_>>(),
-                &[
-                    DiagnosticEntry {
-                        range: Point::new(2, 21)..Point::new(2, 22),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::WARNING,
-                            message: "undefined variable 'A'".to_string(),
-                            is_disk_based: true,
-                            group_id: 6,
-                            is_primary: true,
-                            ..Default::default()
-                        }
-                    },
-                    DiagnosticEntry {
-                        range: Point::new(3, 9)..Point::new(3, 14),
-                        diagnostic: Diagnostic {
-                            severity: DiagnosticSeverity::ERROR,
-                            message: "undefined variable 'BB'".to_string(),
-                            is_disk_based: true,
-                            group_id: 5,
-                            is_primary: true,
-                            ..Default::default()
-                        },
-                    }
-                ]
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let text = concat!(
-            "let one = ;\n", //
-            "let two = \n",
-            "let three = 3;\n",
-        );
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree("/dir", json!({ "a.rs": text })).await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        project.update(cx, |project, cx| {
-            project
-                .update_buffer_diagnostics(
-                    &buffer,
-                    vec![
-                        DiagnosticEntry {
-                            range: PointUtf16::new(0, 10)..PointUtf16::new(0, 10),
-                            diagnostic: Diagnostic {
-                                severity: DiagnosticSeverity::ERROR,
-                                message: "syntax error 1".to_string(),
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: PointUtf16::new(1, 10)..PointUtf16::new(1, 10),
-                            diagnostic: Diagnostic {
-                                severity: DiagnosticSeverity::ERROR,
-                                message: "syntax error 2".to_string(),
-                                ..Default::default()
-                            },
-                        },
-                    ],
-                    None,
-                    cx,
-                )
-                .unwrap();
-        });
-
-        // An empty range is extended forward to include the following character.
-        // At the end of a line, an empty range is extended backward to include
-        // the preceding character.
-        buffer.read_with(cx, |buffer, _| {
-            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
-            assert_eq!(
-                chunks
-                    .iter()
-                    .map(|(s, d)| (s.as_str(), *d))
-                    .collect::<Vec<_>>(),
-                &[
-                    ("let one = ", None),
-                    (";", Some(DiagnosticSeverity::ERROR)),
-                    ("\nlet two =", None),
-                    (" ", Some(DiagnosticSeverity::ERROR)),
-                    ("\nlet three = 3;\n", None)
-                ]
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        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_servers = language.set_fake_lsp_adapter(Default::default());
-
-        let text = "
-            fn a() {
-                f1();
-            }
-            fn b() {
-                f2();
-            }
-            fn c() {
-                f3();
-            }
-        "
-        .unindent();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": text.clone(),
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        let mut fake_server = fake_servers.next().await.unwrap();
-        let lsp_document_version = fake_server
-            .receive_notification::<lsp::notification::DidOpenTextDocument>()
-            .await
-            .text_document
-            .version;
-
-        // Simulate editing the buffer after the language server computes some edits.
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                [(
-                    Point::new(0, 0)..Point::new(0, 0),
-                    "// above first function\n",
-                )],
-                cx,
-            );
-            buffer.edit(
-                [(
-                    Point::new(2, 0)..Point::new(2, 0),
-                    "    // inside first function\n",
-                )],
-                cx,
-            );
-            buffer.edit(
-                [(
-                    Point::new(6, 4)..Point::new(6, 4),
-                    "// inside second function ",
-                )],
-                cx,
-            );
-
-            assert_eq!(
-                buffer.text(),
-                "
-                    // above first function
-                    fn a() {
-                        // inside first function
-                        f1();
-                    }
-                    fn b() {
-                        // inside second function f2();
-                    }
-                    fn c() {
-                        f3();
-                    }
-                "
-                .unindent()
-            );
-        });
-
-        let edits = project
-            .update(cx, |project, cx| {
-                project.edits_from_lsp(
-                    &buffer,
-                    vec![
-                        // replace body of first function
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 0),
-                                lsp::Position::new(3, 0),
-                            ),
-                            new_text: "
-                                fn a() {
-                                    f10();
-                                }
-                            "
-                            .unindent(),
-                        },
-                        // edit inside second function
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(4, 6),
-                                lsp::Position::new(4, 6),
-                            ),
-                            new_text: "00".into(),
-                        },
-                        // edit inside third function via two distinct edits
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(7, 5),
-                                lsp::Position::new(7, 5),
-                            ),
-                            new_text: "4000".into(),
-                        },
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(7, 5),
-                                lsp::Position::new(7, 6),
-                            ),
-                            new_text: "".into(),
-                        },
-                    ],
-                    Some(lsp_document_version),
-                    cx,
-                )
-            })
-            .await
-            .unwrap();
-
-        buffer.update(cx, |buffer, cx| {
-            for (range, new_text) in edits {
-                buffer.edit([(range, new_text)], cx);
-            }
-            assert_eq!(
-                buffer.text(),
-                "
-                    // above first function
-                    fn a() {
-                        // inside first function
-                        f10();
-                    }
-                    fn b() {
-                        // inside second function f200();
-                    }
-                    fn c() {
-                        f4000();
-                    }
-                "
-                .unindent()
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let text = "
-            use a::b;
-            use a::c;
-
-            fn f() {
-                b();
-                c();
-            }
-        "
-        .unindent();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": text.clone(),
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        // Simulate the language server sending us a small edit in the form of a very large diff.
-        // Rust-analyzer does this when performing a merge-imports code action.
-        let edits = project
-            .update(cx, |project, cx| {
-                project.edits_from_lsp(
-                    &buffer,
-                    [
-                        // Replace the first use statement without editing the semicolon.
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 4),
-                                lsp::Position::new(0, 8),
-                            ),
-                            new_text: "a::{b, c}".into(),
-                        },
-                        // Reinsert the remainder of the file between the semicolon and the final
-                        // newline of the file.
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 9),
-                                lsp::Position::new(0, 9),
-                            ),
-                            new_text: "\n\n".into(),
-                        },
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 9),
-                                lsp::Position::new(0, 9),
-                            ),
-                            new_text: "
-                                fn f() {
-                                    b();
-                                    c();
-                                }"
-                            .unindent(),
-                        },
-                        // Delete everything after the first newline of the file.
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(1, 0),
-                                lsp::Position::new(7, 0),
-                            ),
-                            new_text: "".into(),
-                        },
-                    ],
-                    None,
-                    cx,
-                )
-            })
-            .await
-            .unwrap();
-
-        buffer.update(cx, |buffer, cx| {
-            let edits = edits
-                .into_iter()
-                .map(|(range, text)| {
-                    (
-                        range.start.to_point(&buffer)..range.end.to_point(&buffer),
-                        text,
-                    )
-                })
-                .collect::<Vec<_>>();
-
-            assert_eq!(
-                edits,
-                [
-                    (Point::new(0, 4)..Point::new(0, 8), "a::{b, c}".into()),
-                    (Point::new(1, 0)..Point::new(2, 0), "".into())
-                ]
-            );
-
-            for (range, new_text) in edits {
-                buffer.edit([(range, new_text)], cx);
-            }
-            assert_eq!(
-                buffer.text(),
-                "
-                    use a::{b, c};
-
-                    fn f() {
-                        b();
-                        c();
-                    }
-                "
-                .unindent()
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_invalid_edits_from_lsp(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let text = "
-            use a::b;
-            use a::c;
-            
-            fn f() {
-            b();
-            c();
-            }
-            "
-        .unindent();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": text.clone(),
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        // Simulate the language server sending us edits in a non-ordered fashion,
-        // with ranges sometimes being inverted.
-        let edits = project
-            .update(cx, |project, cx| {
-                project.edits_from_lsp(
-                    &buffer,
-                    [
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 9),
-                                lsp::Position::new(0, 9),
-                            ),
-                            new_text: "\n\n".into(),
-                        },
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 8),
-                                lsp::Position::new(0, 4),
-                            ),
-                            new_text: "a::{b, c}".into(),
-                        },
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(1, 0),
-                                lsp::Position::new(7, 0),
-                            ),
-                            new_text: "".into(),
-                        },
-                        lsp::TextEdit {
-                            range: lsp::Range::new(
-                                lsp::Position::new(0, 9),
-                                lsp::Position::new(0, 9),
-                            ),
-                            new_text: "
-                                fn f() {
-                                b();
-                                c();
-                                }"
-                            .unindent(),
-                        },
-                    ],
-                    None,
-                    cx,
-                )
-            })
-            .await
-            .unwrap();
-
-        buffer.update(cx, |buffer, cx| {
-            let edits = edits
-                .into_iter()
-                .map(|(range, text)| {
-                    (
-                        range.start.to_point(&buffer)..range.end.to_point(&buffer),
-                        text,
-                    )
-                })
-                .collect::<Vec<_>>();
-
-            assert_eq!(
-                edits,
-                [
-                    (Point::new(0, 4)..Point::new(0, 8), "a::{b, c}".into()),
-                    (Point::new(1, 0)..Point::new(2, 0), "".into())
-                ]
-            );
-
-            for (range, new_text) in edits {
-                buffer.edit([(range, new_text)], cx);
-            }
-            assert_eq!(
-                buffer.text(),
-                "
-                use a::{b, c};
-                
-                fn f() {
-                b();
-                c();
-                }
-                "
-                .unindent()
-            );
-        });
-    }
-
-    fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
-        buffer: &Buffer,
-        range: Range<T>,
-    ) -> Vec<(String, Option<DiagnosticSeverity>)> {
-        let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
-        for chunk in buffer.snapshot().chunks(range, true) {
-            if chunks.last().map_or(false, |prev_chunk| {
-                prev_chunk.1 == chunk.diagnostic_severity
-            }) {
-                chunks.last_mut().unwrap().0.push_str(chunk.text);
-            } else {
-                chunks.push((chunk.text.to_string(), chunk.diagnostic_severity));
-            }
-        }
-        chunks
-    }
-
-    #[gpui::test]
-    async fn test_search_worktree_without_files(cx: &mut gpui::TestAppContext) {
-        let dir = temp_tree(json!({
-            "root": {
-                "dir1": {},
-                "dir2": {
-                    "dir3": {}
-                }
-            }
-        }));
-
-        let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await;
-        let cancel_flag = Default::default();
-        let results = project
-            .read_with(cx, |project, cx| {
-                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
-            })
-            .await;
-
-        assert!(results.is_empty());
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_definition(cx: &mut gpui::TestAppContext) {
-        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_servers = language.set_fake_lsp_adapter(Default::default());
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.rs": "const fn a() { A }",
-                "b.rs": "const y: i32 = crate::a()",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-
-        let buffer = project
-            .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
-            .await
-            .unwrap();
-
-        let fake_server = fake_servers.next().await.unwrap();
-        fake_server.handle_request::<lsp::request::GotoDefinition, _, _>(|params, _| async move {
-            let params = params.text_document_position_params;
-            assert_eq!(
-                params.text_document.uri.to_file_path().unwrap(),
-                Path::new("/dir/b.rs"),
-            );
-            assert_eq!(params.position, lsp::Position::new(0, 22));
-
-            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
-                lsp::Location::new(
-                    lsp::Url::from_file_path("/dir/a.rs").unwrap(),
-                    lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
-                ),
-            )))
-        });
-
-        let mut definitions = project
-            .update(cx, |project, cx| project.definition(&buffer, 22, cx))
-            .await
-            .unwrap();
-
-        assert_eq!(definitions.len(), 1);
-        let definition = definitions.pop().unwrap();
-        cx.update(|cx| {
-            let target_buffer = definition.target.buffer.read(cx);
-            assert_eq!(
-                target_buffer
-                    .file()
-                    .unwrap()
-                    .as_local()
-                    .unwrap()
-                    .abs_path(cx),
-                Path::new("/dir/a.rs"),
-            );
-            assert_eq!(definition.target.range.to_offset(target_buffer), 9..10);
-            assert_eq!(
-                list_worktrees(&project, cx),
-                [("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
-            );
-
-            drop(definition);
-        });
-        cx.read(|cx| {
-            assert_eq!(list_worktrees(&project, cx), [("/dir/b.rs".as_ref(), true)]);
-        });
-
-        fn list_worktrees<'a>(
-            project: &'a ModelHandle<Project>,
-            cx: &'a AppContext,
-        ) -> Vec<(&'a Path, bool)> {
-            project
-                .read(cx)
-                .worktrees(cx)
-                .map(|worktree| {
-                    let worktree = worktree.read(cx);
-                    (
-                        worktree.as_local().unwrap().abs_path().as_ref(),
-                        worktree.is_visible(),
-                    )
-                })
-                .collect::<Vec<_>>()
-        }
-    }
-
-    #[gpui::test]
-    async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "TypeScript".into(),
-                path_suffixes: vec!["ts".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_typescript::language_typescript()),
-        );
-        let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.ts": "",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
-            .await
-            .unwrap();
-
-        let fake_server = fake_language_servers.next().await.unwrap();
-
-        let text = "let a = b.fqn";
-        buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
-        let completions = project.update(cx, |project, cx| {
-            project.completions(&buffer, text.len(), cx)
-        });
-
-        fake_server
-            .handle_request::<lsp::request::Completion, _, _>(|_, _| async move {
-                Ok(Some(lsp::CompletionResponse::Array(vec![
-                    lsp::CompletionItem {
-                        label: "fullyQualifiedName?".into(),
-                        insert_text: Some("fullyQualifiedName".into()),
-                        ..Default::default()
-                    },
-                ])))
-            })
-            .next()
-            .await;
-        let completions = completions.await.unwrap();
-        let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
-        assert_eq!(completions.len(), 1);
-        assert_eq!(completions[0].new_text, "fullyQualifiedName");
-        assert_eq!(
-            completions[0].old_range.to_offset(&snapshot),
-            text.len() - 3..text.len()
-        );
-
-        let text = "let a = \"atoms/cmp\"";
-        buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
-        let completions = project.update(cx, |project, cx| {
-            project.completions(&buffer, text.len() - 1, cx)
-        });
-
-        fake_server
-            .handle_request::<lsp::request::Completion, _, _>(|_, _| async move {
-                Ok(Some(lsp::CompletionResponse::Array(vec![
-                    lsp::CompletionItem {
-                        label: "component".into(),
-                        ..Default::default()
-                    },
-                ])))
-            })
-            .next()
-            .await;
-        let completions = completions.await.unwrap();
-        let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
-        assert_eq!(completions.len(), 1);
-        assert_eq!(completions[0].new_text, "component");
-        assert_eq!(
-            completions[0].old_range.to_offset(&snapshot),
-            text.len() - 4..text.len() - 1
-        );
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "TypeScript".into(),
-                path_suffixes: vec!["ts".to_string()],
-                ..Default::default()
-            },
-            None,
-        );
-        let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.ts": "a",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
-            .await
-            .unwrap();
-
-        let fake_server = fake_language_servers.next().await.unwrap();
-
-        // Language server returns code actions that contain commands, and not edits.
-        let actions = project.update(cx, |project, cx| project.code_actions(&buffer, 0..0, cx));
-        fake_server
-            .handle_request::<lsp::request::CodeActionRequest, _, _>(|_, _| async move {
-                Ok(Some(vec![
-                    lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
-                        title: "The code action".into(),
-                        command: Some(lsp::Command {
-                            title: "The command".into(),
-                            command: "_the/command".into(),
-                            arguments: Some(vec![json!("the-argument")]),
-                        }),
-                        ..Default::default()
-                    }),
-                    lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
-                        title: "two".into(),
-                        ..Default::default()
-                    }),
-                ]))
-            })
-            .next()
-            .await;
-
-        let action = actions.await.unwrap()[0].clone();
-        let apply = project.update(cx, |project, cx| {
-            project.apply_code_action(buffer.clone(), action, true, cx)
-        });
-
-        // Resolving the code action does not populate its edits. In absence of
-        // edits, we must execute the given command.
-        fake_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
-            |action, _| async move { Ok(action) },
-        );
-
-        // While executing the command, the language server sends the editor
-        // a `workspaceEdit` request.
-        fake_server
-            .handle_request::<lsp::request::ExecuteCommand, _, _>({
-                let fake = fake_server.clone();
-                move |params, _| {
-                    assert_eq!(params.command, "_the/command");
-                    let fake = fake.clone();
-                    async move {
-                        fake.server
-                            .request::<lsp::request::ApplyWorkspaceEdit>(
-                                lsp::ApplyWorkspaceEditParams {
-                                    label: None,
-                                    edit: lsp::WorkspaceEdit {
-                                        changes: Some(
-                                            [(
-                                                lsp::Url::from_file_path("/dir/a.ts").unwrap(),
-                                                vec![lsp::TextEdit {
-                                                    range: lsp::Range::new(
-                                                        lsp::Position::new(0, 0),
-                                                        lsp::Position::new(0, 0),
-                                                    ),
-                                                    new_text: "X".into(),
-                                                }],
-                                            )]
-                                            .into_iter()
-                                            .collect(),
-                                        ),
-                                        ..Default::default()
-                                    },
-                                },
-                            )
-                            .await
-                            .unwrap();
-                        Ok(Some(json!(null)))
-                    }
-                }
-            })
-            .next()
-            .await;
-
-        // Applying the code action returns a project transaction containing the edits
-        // sent by the language server in its `workspaceEdit` request.
-        let transaction = apply.await.unwrap();
-        assert!(transaction.0.contains_key(&buffer));
-        buffer.update(cx, |buffer, cx| {
-            assert_eq!(buffer.text(), "Xa");
-            buffer.undo(cx);
-            assert_eq!(buffer.text(), "a");
-        });
-    }
-
-    #[gpui::test]
-    async fn test_save_file(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "file1": "the old contents",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
-            .await
-            .unwrap();
-        buffer
-            .update(cx, |buffer, cx| {
-                assert_eq!(buffer.text(), "the old contents");
-                buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], cx);
-                buffer.save(cx)
-            })
-            .await
-            .unwrap();
-
-        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
-        assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
-    }
-
-    #[gpui::test]
-    async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "file1": "the old contents",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/dir/file1".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
-            .await
-            .unwrap();
-        buffer
-            .update(cx, |buffer, cx| {
-                buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], cx);
-                buffer.save(cx)
-            })
-            .await
-            .unwrap();
-
-        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
-        assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
-    }
-
-    #[gpui::test]
-    async fn test_save_as(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree("/dir", json!({})).await;
-
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-        let buffer = project.update(cx, |project, cx| {
-            project.create_buffer("", None, cx).unwrap()
-        });
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, "abc")], cx);
-            assert!(buffer.is_dirty());
-            assert!(!buffer.has_conflict());
-        });
-        project
-            .update(cx, |project, cx| {
-                project.save_buffer_as(buffer.clone(), "/dir/file1".into(), cx)
-            })
-            .await
-            .unwrap();
-        assert_eq!(fs.load(Path::new("/dir/file1")).await.unwrap(), "abc");
-        buffer.read_with(cx, |buffer, cx| {
-            assert_eq!(buffer.file().unwrap().full_path(cx), Path::new("dir/file1"));
-            assert!(!buffer.is_dirty());
-            assert!(!buffer.has_conflict());
-        });
-
-        let opened_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/dir/file1", cx)
-            })
-            .await
-            .unwrap();
-        assert_eq!(opened_buffer, buffer);
-    }
-
-    #[gpui::test(retries = 5)]
-    async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
-        let dir = temp_tree(json!({
-            "a": {
-                "file1": "",
-                "file2": "",
-                "file3": "",
-            },
-            "b": {
-                "c": {
-                    "file4": "",
-                    "file5": "",
-                }
-            }
-        }));
-
-        let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await;
-        let rpc = project.read_with(cx, |p, _| p.client.clone());
-
-        let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| {
-            let buffer = project.update(cx, |p, cx| p.open_local_buffer(dir.path().join(path), cx));
-            async move { buffer.await.unwrap() }
-        };
-        let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
-            project.read_with(cx, |project, cx| {
-                let tree = project.worktrees(cx).next().unwrap();
-                tree.read(cx)
-                    .entry_for_path(path)
-                    .expect(&format!("no entry for path {}", path))
-                    .id
-            })
-        };
-
-        let buffer2 = buffer_for_path("a/file2", cx).await;
-        let buffer3 = buffer_for_path("a/file3", cx).await;
-        let buffer4 = buffer_for_path("b/c/file4", cx).await;
-        let buffer5 = buffer_for_path("b/c/file5", cx).await;
-
-        let file2_id = id_for_path("a/file2", &cx);
-        let file3_id = id_for_path("a/file3", &cx);
-        let file4_id = id_for_path("b/c/file4", &cx);
-
-        // Create a remote copy of this worktree.
-        let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
-        let initial_snapshot = tree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
-        let (remote, load_task) = cx.update(|cx| {
-            Worktree::remote(
-                1,
-                1,
-                initial_snapshot.to_proto(&Default::default(), true),
-                rpc.clone(),
-                cx,
-            )
-        });
-        // tree
-        load_task.await;
-
-        cx.read(|cx| {
-            assert!(!buffer2.read(cx).is_dirty());
-            assert!(!buffer3.read(cx).is_dirty());
-            assert!(!buffer4.read(cx).is_dirty());
-            assert!(!buffer5.read(cx).is_dirty());
-        });
-
-        // Rename and delete files and directories.
-        tree.flush_fs_events(&cx).await;
-        std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
-        std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
-        std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
-        std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
-        tree.flush_fs_events(&cx).await;
-
-        let expected_paths = vec![
-            "a",
-            "a/file1",
-            "a/file2.new",
-            "b",
-            "d",
-            "d/file3",
-            "d/file4",
-        ];
-
-        cx.read(|app| {
-            assert_eq!(
-                tree.read(app)
-                    .paths()
-                    .map(|p| p.to_str().unwrap())
-                    .collect::<Vec<_>>(),
-                expected_paths
-            );
-
-            assert_eq!(id_for_path("a/file2.new", &cx), file2_id);
-            assert_eq!(id_for_path("d/file3", &cx), file3_id);
-            assert_eq!(id_for_path("d/file4", &cx), file4_id);
-
-            assert_eq!(
-                buffer2.read(app).file().unwrap().path().as_ref(),
-                Path::new("a/file2.new")
-            );
-            assert_eq!(
-                buffer3.read(app).file().unwrap().path().as_ref(),
-                Path::new("d/file3")
-            );
-            assert_eq!(
-                buffer4.read(app).file().unwrap().path().as_ref(),
-                Path::new("d/file4")
-            );
-            assert_eq!(
-                buffer5.read(app).file().unwrap().path().as_ref(),
-                Path::new("b/c/file5")
-            );
-
-            assert!(!buffer2.read(app).file().unwrap().is_deleted());
-            assert!(!buffer3.read(app).file().unwrap().is_deleted());
-            assert!(!buffer4.read(app).file().unwrap().is_deleted());
-            assert!(buffer5.read(app).file().unwrap().is_deleted());
-        });
-
-        // Update the remote worktree. Check that it becomes consistent with the
-        // local worktree.
-        remote.update(cx, |remote, cx| {
-            let update_message = tree.read(cx).as_local().unwrap().snapshot().build_update(
-                &initial_snapshot,
-                1,
-                1,
-                true,
-            );
-            remote
-                .as_remote_mut()
-                .unwrap()
-                .snapshot
-                .apply_remote_update(update_message)
-                .unwrap();
-
-            assert_eq!(
-                remote
-                    .paths()
-                    .map(|p| p.to_str().unwrap())
-                    .collect::<Vec<_>>(),
-                expected_paths
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "a.txt": "a-contents",
-                "b.txt": "b-contents",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-
-        // Spawn multiple tasks to open paths, repeating some paths.
-        let (buffer_a_1, buffer_b, buffer_a_2) = project.update(cx, |p, cx| {
-            (
-                p.open_local_buffer("/dir/a.txt", cx),
-                p.open_local_buffer("/dir/b.txt", cx),
-                p.open_local_buffer("/dir/a.txt", cx),
-            )
-        });
-
-        let buffer_a_1 = buffer_a_1.await.unwrap();
-        let buffer_a_2 = buffer_a_2.await.unwrap();
-        let buffer_b = buffer_b.await.unwrap();
-        assert_eq!(buffer_a_1.read_with(cx, |b, _| b.text()), "a-contents");
-        assert_eq!(buffer_b.read_with(cx, |b, _| b.text()), "b-contents");
-
-        // There is only one buffer per path.
-        let buffer_a_id = buffer_a_1.id();
-        assert_eq!(buffer_a_2.id(), buffer_a_id);
-
-        // Open the same path again while it is still open.
-        drop(buffer_a_1);
-        let buffer_a_3 = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/a.txt", cx))
-            .await
-            .unwrap();
-
-        // There's still only one buffer per path.
-        assert_eq!(buffer_a_3.id(), buffer_a_id);
-    }
-
-    #[gpui::test]
-    async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "file1": "abc",
-                "file2": "def",
-                "file3": "ghi",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-
-        let buffer1 = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
-            .await
-            .unwrap();
-        let events = Rc::new(RefCell::new(Vec::new()));
-
-        // initially, the buffer isn't dirty.
-        buffer1.update(cx, |buffer, cx| {
-            cx.subscribe(&buffer1, {
-                let events = events.clone();
-                move |_, _, event, _| match event {
-                    BufferEvent::Operation(_) => {}
-                    _ => events.borrow_mut().push(event.clone()),
-                }
-            })
-            .detach();
-
-            assert!(!buffer.is_dirty());
-            assert!(events.borrow().is_empty());
-
-            buffer.edit([(1..2, "")], cx);
-        });
-
-        // after the first edit, the buffer is dirty, and emits a dirtied event.
-        buffer1.update(cx, |buffer, cx| {
-            assert!(buffer.text() == "ac");
-            assert!(buffer.is_dirty());
-            assert_eq!(
-                *events.borrow(),
-                &[language::Event::Edited, language::Event::DirtyChanged]
-            );
-            events.borrow_mut().clear();
-            buffer.did_save(
-                buffer.version(),
-                buffer.as_rope().fingerprint(),
-                buffer.file().unwrap().mtime(),
-                None,
-                cx,
-            );
-        });
-
-        // after saving, the buffer is not dirty, and emits a saved event.
-        buffer1.update(cx, |buffer, cx| {
-            assert!(!buffer.is_dirty());
-            assert_eq!(*events.borrow(), &[language::Event::Saved]);
-            events.borrow_mut().clear();
-
-            buffer.edit([(1..1, "B")], cx);
-            buffer.edit([(2..2, "D")], cx);
-        });
-
-        // after editing again, the buffer is dirty, and emits another dirty event.
-        buffer1.update(cx, |buffer, cx| {
-            assert!(buffer.text() == "aBDc");
-            assert!(buffer.is_dirty());
-            assert_eq!(
-                *events.borrow(),
-                &[
-                    language::Event::Edited,
-                    language::Event::DirtyChanged,
-                    language::Event::Edited,
-                ],
-            );
-            events.borrow_mut().clear();
-
-            // After restoring the buffer to its previously-saved state,
-            // the buffer is not considered dirty anymore.
-            buffer.edit([(1..3, "")], cx);
-            assert!(buffer.text() == "ac");
-            assert!(!buffer.is_dirty());
-        });
-
-        assert_eq!(
-            *events.borrow(),
-            &[language::Event::Edited, language::Event::DirtyChanged]
-        );
-
-        // When a file is deleted, the buffer is considered dirty.
-        let events = Rc::new(RefCell::new(Vec::new()));
-        let buffer2 = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx))
-            .await
-            .unwrap();
-        buffer2.update(cx, |_, cx| {
-            cx.subscribe(&buffer2, {
-                let events = events.clone();
-                move |_, _, event, _| events.borrow_mut().push(event.clone())
-            })
-            .detach();
-        });
-
-        fs.remove_file("/dir/file2".as_ref(), Default::default())
-            .await
-            .unwrap();
-        buffer2.condition(&cx, |b, _| b.is_dirty()).await;
-        assert_eq!(
-            *events.borrow(),
-            &[
-                language::Event::DirtyChanged,
-                language::Event::FileHandleChanged
-            ]
-        );
-
-        // When a file is already dirty when deleted, we don't emit a Dirtied event.
-        let events = Rc::new(RefCell::new(Vec::new()));
-        let buffer3 = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/file3", cx))
-            .await
-            .unwrap();
-        buffer3.update(cx, |_, cx| {
-            cx.subscribe(&buffer3, {
-                let events = events.clone();
-                move |_, _, event, _| events.borrow_mut().push(event.clone())
-            })
-            .detach();
-        });
-
-        buffer3.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, "x")], cx);
-        });
-        events.borrow_mut().clear();
-        fs.remove_file("/dir/file3".as_ref(), Default::default())
-            .await
-            .unwrap();
-        buffer3
-            .condition(&cx, |_, _| !events.borrow().is_empty())
-            .await;
-        assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
-        cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
-    }
-
-    #[gpui::test]
-    async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
-        let initial_contents = "aaa\nbbbbb\nc\n";
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "the-file": initial_contents,
-            }),
-        )
-        .await;
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/dir/the-file", cx))
-            .await
-            .unwrap();
-
-        let anchors = (0..3)
-            .map(|row| buffer.read_with(cx, |b, _| b.anchor_before(Point::new(row, 1))))
-            .collect::<Vec<_>>();
-
-        // Change the file on disk, adding two new lines of text, and removing
-        // one line.
-        buffer.read_with(cx, |buffer, _| {
-            assert!(!buffer.is_dirty());
-            assert!(!buffer.has_conflict());
-        });
-        let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
-        fs.save("/dir/the-file".as_ref(), &new_contents.into())
-            .await
-            .unwrap();
-
-        // Because the buffer was not modified, it is reloaded from disk. Its
-        // contents are edited according to the diff between the old and new
-        // file contents.
-        buffer
-            .condition(&cx, |buffer, _| buffer.text() == new_contents)
-            .await;
-
-        buffer.update(cx, |buffer, _| {
-            assert_eq!(buffer.text(), new_contents);
-            assert!(!buffer.is_dirty());
-            assert!(!buffer.has_conflict());
-
-            let anchor_positions = anchors
-                .iter()
-                .map(|anchor| anchor.to_point(&*buffer))
-                .collect::<Vec<_>>();
-            assert_eq!(
-                anchor_positions,
-                [Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
-            );
-        });
-
-        // Modify the buffer
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, " ")], cx);
-            assert!(buffer.is_dirty());
-            assert!(!buffer.has_conflict());
-        });
-
-        // Change the file on disk again, adding blank lines to the beginning.
-        fs.save(
-            "/dir/the-file".as_ref(),
-            &"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
-        )
-        .await
-        .unwrap();
-
-        // Because the buffer is modified, it doesn't reload from disk, but is
-        // marked as having a conflict.
-        buffer
-            .condition(&cx, |buffer, _| buffer.has_conflict())
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/the-dir",
-            json!({
-                "a.rs": "
-                    fn foo(mut v: Vec<usize>) {
-                        for x in &v {
-                            v.push(1);
-                        }
-                    }
-                "
-                .unindent(),
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/the-dir".as_ref()], cx).await;
-        let buffer = project
-            .update(cx, |p, cx| p.open_local_buffer("/the-dir/a.rs", cx))
-            .await
-            .unwrap();
-
-        let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
-        let message = lsp::PublishDiagnosticsParams {
-            uri: buffer_uri.clone(),
-            diagnostics: vec![
-                lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
-                    severity: Some(DiagnosticSeverity::WARNING),
-                    message: "error 1".to_string(),
-                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
-                        location: lsp::Location {
-                            uri: buffer_uri.clone(),
-                            range: lsp::Range::new(
-                                lsp::Position::new(1, 8),
-                                lsp::Position::new(1, 9),
-                            ),
-                        },
-                        message: "error 1 hint 1".to_string(),
-                    }]),
-                    ..Default::default()
-                },
-                lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
-                    severity: Some(DiagnosticSeverity::HINT),
-                    message: "error 1 hint 1".to_string(),
-                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
-                        location: lsp::Location {
-                            uri: buffer_uri.clone(),
-                            range: lsp::Range::new(
-                                lsp::Position::new(1, 8),
-                                lsp::Position::new(1, 9),
-                            ),
-                        },
-                        message: "original diagnostic".to_string(),
-                    }]),
-                    ..Default::default()
-                },
-                lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)),
-                    severity: Some(DiagnosticSeverity::ERROR),
-                    message: "error 2".to_string(),
-                    related_information: Some(vec![
-                        lsp::DiagnosticRelatedInformation {
-                            location: lsp::Location {
-                                uri: buffer_uri.clone(),
-                                range: lsp::Range::new(
-                                    lsp::Position::new(1, 13),
-                                    lsp::Position::new(1, 15),
-                                ),
-                            },
-                            message: "error 2 hint 1".to_string(),
-                        },
-                        lsp::DiagnosticRelatedInformation {
-                            location: lsp::Location {
-                                uri: buffer_uri.clone(),
-                                range: lsp::Range::new(
-                                    lsp::Position::new(1, 13),
-                                    lsp::Position::new(1, 15),
-                                ),
-                            },
-                            message: "error 2 hint 2".to_string(),
-                        },
-                    ]),
-                    ..Default::default()
-                },
-                lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
-                    severity: Some(DiagnosticSeverity::HINT),
-                    message: "error 2 hint 1".to_string(),
-                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
-                        location: lsp::Location {
-                            uri: buffer_uri.clone(),
-                            range: lsp::Range::new(
-                                lsp::Position::new(2, 8),
-                                lsp::Position::new(2, 17),
-                            ),
-                        },
-                        message: "original diagnostic".to_string(),
-                    }]),
-                    ..Default::default()
-                },
-                lsp::Diagnostic {
-                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
-                    severity: Some(DiagnosticSeverity::HINT),
-                    message: "error 2 hint 2".to_string(),
-                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
-                        location: lsp::Location {
-                            uri: buffer_uri.clone(),
-                            range: lsp::Range::new(
-                                lsp::Position::new(2, 8),
-                                lsp::Position::new(2, 17),
-                            ),
-                        },
-                        message: "original diagnostic".to_string(),
-                    }]),
-                    ..Default::default()
-                },
-            ],
-            version: None,
-        };
-
-        project
-            .update(cx, |p, cx| p.update_diagnostics(0, message, &[], cx))
-            .unwrap();
-        let buffer = buffer.read_with(cx, |buffer, _| buffer.snapshot());
-
-        assert_eq!(
-            buffer
-                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
-                .collect::<Vec<_>>(),
-            &[
-                DiagnosticEntry {
-                    range: Point::new(1, 8)..Point::new(1, 9),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::WARNING,
-                        message: "error 1".to_string(),
-                        group_id: 0,
-                        is_primary: true,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(1, 8)..Point::new(1, 9),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 1 hint 1".to_string(),
-                        group_id: 0,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(1, 13)..Point::new(1, 15),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 2 hint 1".to_string(),
-                        group_id: 1,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(1, 13)..Point::new(1, 15),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 2 hint 2".to_string(),
-                        group_id: 1,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(2, 8)..Point::new(2, 17),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::ERROR,
-                        message: "error 2".to_string(),
-                        group_id: 1,
-                        is_primary: true,
-                        ..Default::default()
-                    }
-                }
-            ]
-        );
-
-        assert_eq!(
-            buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
-            &[
-                DiagnosticEntry {
-                    range: Point::new(1, 8)..Point::new(1, 9),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::WARNING,
-                        message: "error 1".to_string(),
-                        group_id: 0,
-                        is_primary: true,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(1, 8)..Point::new(1, 9),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 1 hint 1".to_string(),
-                        group_id: 0,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-            ]
-        );
-        assert_eq!(
-            buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
-            &[
-                DiagnosticEntry {
-                    range: Point::new(1, 13)..Point::new(1, 15),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 2 hint 1".to_string(),
-                        group_id: 1,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(1, 13)..Point::new(1, 15),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: "error 2 hint 2".to_string(),
-                        group_id: 1,
-                        is_primary: false,
-                        ..Default::default()
-                    }
-                },
-                DiagnosticEntry {
-                    range: Point::new(2, 8)..Point::new(2, 17),
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::ERROR,
-                        message: "error 2".to_string(),
-                        group_id: 1,
-                        is_primary: true,
-                        ..Default::default()
-                    }
-                }
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_rename(cx: &mut gpui::TestAppContext) {
-        cx.foreground().forbid_parking();
-
-        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_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
-                    prepare_provider: Some(true),
-                    work_done_progress_options: Default::default(),
-                })),
-                ..Default::default()
-            },
-            ..Default::default()
-        }));
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "one.rs": "const ONE: usize = 1;",
-                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
-        let buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/dir/one.rs", cx)
-            })
-            .await
-            .unwrap();
-
-        let fake_server = fake_servers.next().await.unwrap();
-
-        let response = project.update(cx, |project, cx| {
-            project.prepare_rename(buffer.clone(), 7, cx)
-        });
-        fake_server
-            .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
-                assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
-                assert_eq!(params.position, lsp::Position::new(0, 7));
-                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
-                    lsp::Position::new(0, 6),
-                    lsp::Position::new(0, 9),
-                ))))
-            })
-            .next()
-            .await
-            .unwrap();
-        let range = response.await.unwrap().unwrap();
-        let range = buffer.read_with(cx, |buffer, _| range.to_offset(buffer));
-        assert_eq!(range, 6..9);
-
-        let response = project.update(cx, |project, cx| {
-            project.perform_rename(buffer.clone(), 7, "THREE".to_string(), true, cx)
-        });
-        fake_server
-            .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
-                assert_eq!(
-                    params.text_document_position.text_document.uri.as_str(),
-                    "file:///dir/one.rs"
-                );
-                assert_eq!(
-                    params.text_document_position.position,
-                    lsp::Position::new(0, 7)
-                );
-                assert_eq!(params.new_name, "THREE");
-                Ok(Some(lsp::WorkspaceEdit {
-                    changes: Some(
-                        [
-                            (
-                                lsp::Url::from_file_path("/dir/one.rs").unwrap(),
-                                vec![lsp::TextEdit::new(
-                                    lsp::Range::new(
-                                        lsp::Position::new(0, 6),
-                                        lsp::Position::new(0, 9),
-                                    ),
-                                    "THREE".to_string(),
-                                )],
-                            ),
-                            (
-                                lsp::Url::from_file_path("/dir/two.rs").unwrap(),
-                                vec![
-                                    lsp::TextEdit::new(
-                                        lsp::Range::new(
-                                            lsp::Position::new(0, 24),
-                                            lsp::Position::new(0, 27),
-                                        ),
-                                        "THREE".to_string(),
-                                    ),
-                                    lsp::TextEdit::new(
-                                        lsp::Range::new(
-                                            lsp::Position::new(0, 35),
-                                            lsp::Position::new(0, 38),
-                                        ),
-                                        "THREE".to_string(),
-                                    ),
-                                ],
-                            ),
-                        ]
-                        .into_iter()
-                        .collect(),
-                    ),
-                    ..Default::default()
-                }))
-            })
-            .next()
-            .await
-            .unwrap();
-        let mut transaction = response.await.unwrap().0;
-        assert_eq!(transaction.len(), 2);
-        assert_eq!(
-            transaction
-                .remove_entry(&buffer)
-                .unwrap()
-                .0
-                .read_with(cx, |buffer, _| buffer.text()),
-            "const THREE: usize = 1;"
-        );
-        assert_eq!(
-            transaction
-                .into_keys()
-                .next()
-                .unwrap()
-                .read_with(cx, |buffer, _| buffer.text()),
-            "const TWO: usize = one::THREE + one::THREE;"
-        );
-    }
-
-    #[gpui::test]
-    async fn test_search(cx: &mut gpui::TestAppContext) {
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/dir",
-            json!({
-                "one.rs": "const ONE: usize = 1;",
-                "two.rs": "const TWO: usize = one::ONE + one::ONE;",
-                "three.rs": "const THREE: usize = one::ONE + two::TWO;",
-                "four.rs": "const FOUR: usize = one::ONE + three::THREE;",
-            }),
-        )
-        .await;
-        let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
-        assert_eq!(
-            search(&project, SearchQuery::text("TWO", false, true), cx)
-                .await
-                .unwrap(),
-            HashMap::from_iter([
-                ("two.rs".to_string(), vec![6..9]),
-                ("three.rs".to_string(), vec![37..40])
-            ])
-        );
-
-        let buffer_4 = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/dir/four.rs", cx)
-            })
-            .await
-            .unwrap();
-        buffer_4.update(cx, |buffer, cx| {
-            let text = "two::TWO";
-            buffer.edit([(20..28, text), (31..43, text)], cx);
-        });
-
-        assert_eq!(
-            search(&project, SearchQuery::text("TWO", false, true), cx)
-                .await
-                .unwrap(),
-            HashMap::from_iter([
-                ("two.rs".to_string(), vec![6..9]),
-                ("three.rs".to_string(), vec![37..40]),
-                ("four.rs".to_string(), vec![25..28, 36..39])
-            ])
-        );
-
-        async fn search(
-            project: &ModelHandle<Project>,
-            query: SearchQuery,
-            cx: &mut gpui::TestAppContext,
-        ) -> Result<HashMap<String, Vec<Range<usize>>>> {
-            let results = project
-                .update(cx, |project, cx| project.search(query, cx))
-                .await?;
-
-            Ok(results
-                .into_iter()
-                .map(|(buffer, ranges)| {
-                    buffer.read_with(cx, |buffer, _| {
-                        let path = buffer.file().unwrap().path().to_string_lossy().to_string();
-                        let ranges = ranges
-                            .into_iter()
-                            .map(|range| range.to_offset(buffer))
-                            .collect::<Vec<_>>();
-                        (path, ranges)
-                    })
-                })
-                .collect())
-        }
-    }
-}

crates/project/src/project_tests.rs 🔗

@@ -86,7 +86,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
         },
         None,
     );
-    let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(FakeLspAdapter {
+    let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
         name: "the-rust-language-server",
         capabilities: lsp::ServerCapabilities {
             completion_provider: Some(lsp::CompletionOptions {
@@ -96,8 +96,8 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
             ..Default::default()
         },
         ..Default::default()
-    });
-    let mut fake_json_servers = json_language.set_fake_lsp_adapter(FakeLspAdapter {
+    }));
+    let mut fake_json_servers = json_language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
         name: "the-json-language-server",
         capabilities: lsp::ServerCapabilities {
             completion_provider: Some(lsp::CompletionOptions {
@@ -107,7 +107,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
             ..Default::default()
         },
         ..Default::default()
-    });
+    }));
 
     let fs = FakeFs::new(cx.background());
     fs.insert_tree(
@@ -611,11 +611,11 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
         },
         Some(tree_sitter_rust::language()),
     );
-    let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
-        disk_based_diagnostics_progress_token: Some(progress_token),
-        disk_based_diagnostics_sources: &["disk"],
+    let mut fake_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+        disk_based_diagnostics_progress_token: Some(progress_token.into()),
+        disk_based_diagnostics_sources: vec!["disk".into()],
         ..Default::default()
-    });
+    }));
 
     let fs = FakeFs::new(cx.background());
     fs.insert_tree(
@@ -734,11 +734,11 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
         },
         None,
     );
-    let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
-        disk_based_diagnostics_sources: &["disk"],
-        disk_based_diagnostics_progress_token: Some(progress_token),
+    let mut fake_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+        disk_based_diagnostics_sources: vec!["disk".into()],
+        disk_based_diagnostics_progress_token: Some(progress_token.into()),
         ..Default::default()
-    });
+    }));
 
     let fs = FakeFs::new(cx.background());
     fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
@@ -813,10 +813,10 @@ async fn test_toggling_enable_language_server(
         },
         None,
     );
-    let mut fake_rust_servers = rust.set_fake_lsp_adapter(FakeLspAdapter {
+    let mut fake_rust_servers = rust.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
         name: "rust-lsp",
         ..Default::default()
-    });
+    }));
     let mut js = Language::new(
         LanguageConfig {
             name: Arc::from("JavaScript"),
@@ -825,10 +825,10 @@ async fn test_toggling_enable_language_server(
         },
         None,
     );
-    let mut fake_js_servers = js.set_fake_lsp_adapter(FakeLspAdapter {
+    let mut fake_js_servers = js.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
         name: "js-lsp",
         ..Default::default()
-    });
+    }));
 
     let fs = FakeFs::new(cx.background());
     fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
@@ -934,10 +934,10 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
         },
         Some(tree_sitter_rust::language()),
     );
-    let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
-        disk_based_diagnostics_sources: &["disk"],
+    let mut fake_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+        disk_based_diagnostics_sources: vec!["disk".into()],
         ..Default::default()
-    });
+    }));
 
     let text = "
         fn a() { A }
@@ -2841,7 +2841,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
         },
         Some(tree_sitter_rust::language()),
     );
-    let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
+    let mut fake_servers = language.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
         capabilities: lsp::ServerCapabilities {
             rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
                 prepare_provider: Some(true),
@@ -2850,7 +2850,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
             ..Default::default()
         },
         ..Default::default()
-    });
+    }));
 
     let fs = FakeFs::new(cx.background());
     fs.insert_tree(