diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 404ed265b3..262171e510 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -712,61 +712,64 @@ impl Project { } fn on_settings_changed(&mut self, cx: &mut ModelContext<'_, Self>) { - let settings = cx.global::(); - self.lsp_settings_changed = Some(cx.spawn(|project, cx| async { - let language_servers_to_start = project.update(&mut cx, |project, cx| { - let mut language_servers_to_start = Vec::new(); - for buffer in self.opened_buffers.values() { - if let Some(buffer) = buffer.upgrade(cx) { - let buffer = buffer.read(cx); - if let Some((file, language)) = - File::from_dyn(buffer.file()).zip(buffer.language()) - { - if settings.enable_language_server(Some(&language.name())) { - let worktree = file.worktree.read(cx); - language_servers_to_start.push(( - worktree.id(), - worktree.as_local().unwrap().abs_path().clone(), - language.clone(), - )); - } - } - } - } - language_servers_to_start - }); + // let settings = cx.global::(); + // self.lsp_settings_changed = Some(cx.spawn(|project, cx| async { + // let language_servers_to_start = project.update(&mut cx, |project, cx| { + // let mut language_servers_to_start = Vec::new(); + // for buffer in self.opened_buffers.values() { + // if let Some(buffer) = buffer.upgrade(cx) { + // let buffer = buffer.read(cx); + // if let Some((file, language)) = + // File::from_dyn(buffer.file()).zip(buffer.language()) + // { + // if settings.enable_language_server(Some(&language.name())) { + // let worktree = file.worktree.read(cx); + // language_servers_to_start.push(( + // worktree.id(), + // worktree.as_local().unwrap().abs_path().clone(), + // language.clone(), + // )); + // } + // } + // } + // } + // language_servers_to_start + // }); - let mut language_servers_to_stop = Vec::new(); - for language in self.languages.to_vec() { - if let Some(lsp_adapter) = language.lsp_adapter() { - if !settings.enable_language_server(Some(&language.name())) { - let lsp_name = lsp_adapter.name().await; - for (worktree_id, started_lsp_name) in self.started_language_servers.keys() - { - if lsp_name == *started_lsp_name { - language_servers_to_stop - .push((*worktree_id, started_lsp_name.clone())); - } - } - } - } - } + // let mut language_servers_to_stop = Vec::new(); + // for language in self.languages.to_vec() { + // if let Some(lsp_adapter) = language.lsp_adapter() { + // if !settings.enable_language_server(Some(&language.name())) { + // let lsp_name = lsp_adapter.name().await; + // for (worktree_id, started_lsp_name) in self.started_language_servers.keys() + // { + // if lsp_name == *started_lsp_name { + // language_servers_to_stop + // .push((*worktree_id, started_lsp_name.clone())); + // } + // } + // } + // } + // } - project.update(&mut cx, |project, cx| { - // Stop all newly-disabled language servers. - for (worktree_id, adapter_name) in language_servers_to_stop { - self.stop_language_server(worktree_id, adapter_name, cx) - .detach(); - } + // project.update(&mut cx, |project, cx| { + // // Stop all newly-disabled language servers. + // for (worktree_id, adapter_name) in language_servers_to_stop { + // self.stop_language_server(worktree_id, adapter_name, cx) + // .detach(); + // } - // Start all the newly-enabled language servers. - for (worktree_id, worktree_path, language) in language_servers_to_start { - self.start_language_server(worktree_id, worktree_path, language, cx); - } + // // Start all the newly-enabled language servers. + // for (worktree_id, worktree_path, language) in language_servers_to_start { + // self.start_language_server(worktree_id, worktree_path, language, cx); + // } - cx.notify(); - }); - })) + // cx.notify(); + // }); + // })) + + // TODO(isaac): uncomment the above + todo!() } pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option> { @@ -2186,6 +2189,7 @@ impl Project { language_server.clone(), cx, ) + } }) .detach(); @@ -2496,9 +2500,12 @@ impl Project { return; } + let same_token = + Some(token.as_ref()) == disk_based_diagnostics_progress_token.as_ref().map(|x| &**x); + match progress { lsp::WorkDoneProgress::Begin(report) => { - if Some(token) == disk_based_diagnostics_progress_token { + if same_token { language_server_status.has_pending_diagnostic_updates = true; self.disk_based_diagnostics_started(server_id, cx); self.broadcast_language_server_update( @@ -2529,7 +2536,7 @@ impl Project { } } lsp::WorkDoneProgress::Report(report) => { - if Some(token) != disk_based_diagnostics_progress_token { + if !same_token { self.on_lsp_work_progress( server_id, token.clone(), @@ -2555,7 +2562,7 @@ impl Project { lsp::WorkDoneProgress::End(_) => { language_server_status.progress_tokens.remove(&token); - if Some(token) == disk_based_diagnostics_progress_token { + if same_token { language_server_status.has_pending_diagnostic_updates = false; self.disk_based_diagnostics_finished(server_id, cx); self.broadcast_language_server_update( @@ -3299,16 +3306,12 @@ impl Project { return Ok(Default::default()); }; - struct PartialSymbol - where - F1: Future, - F2: Future>, - { + struct PartialSymbol { source_worktree_id: WorktreeId, worktree_id: WorktreeId, - language_server_name: F1, + adapter: Arc, path: PathBuf, - label: Option, + language: Option>, name: String, kind: lsp::SymbolKind, range: Range, @@ -3334,23 +3337,17 @@ impl Project { path = relativize_path(&worktree_abs_path, &abs_path); } - let label = match this.languages.select_language(&path) { - Some(language) => Some( - language.label_for_symbol(&lsp_symbol.name, lsp_symbol.kind), - ), - None => None, - }; - + let language = this.languages.select_language(&path).clone(); let signature = this.symbol_signature(worktree_id, &path); - let language_server_name = adapter.name(); partial_symbols.push(PartialSymbol { source_worktree_id, worktree_id, - language_server_name, + // TODO: just pass out single adapter? + adapter: adapter.clone(), name: lsp_symbol.name, kind: lsp_symbol.kind, - label, + language, path, range: range_from_lsp(lsp_symbol.location.range), signature, @@ -3363,16 +3360,18 @@ impl Project { let mut symbols = Vec::new(); for ps in partial_symbols.into_iter() { - let label = match ps.label { - Some(label) => label.await, + let label = match ps.language { + Some(language) => language.label_for_symbol(&ps.name, ps.kind).await, None => None, } .unwrap_or_else(|| CodeLabel::plain(ps.name.clone(), None)); + let language_server_name = ps.adapter.name().await; + symbols.push(Symbol { source_worktree_id: ps.source_worktree_id, worktree_id: ps.worktree_id, - language_server_name: ps.language_server_name.await, + language_server_name, name: ps.name, kind: ps.kind, label, @@ -3394,10 +3393,11 @@ impl Project { let mut symbols = Vec::new(); if let Some(this) = this.upgrade(&cx) { let new_symbols = this.read_with(&cx, |this, _| { - response - .symbols - .into_iter() - .map(|symbol| this.deserialize_symbol(symbol)) + let mut new_symbols = Vec::new(); + for symbol in response.symbols.into_iter() { + new_symbols.push(this.deserialize_symbol(symbol)); + } + new_symbols }); for new_symbol in new_symbols { if let Some(new_symbol) = new_symbol.await.ok() { @@ -3533,10 +3533,10 @@ impl Project { Default::default() }; - struct PartialCompletion>> { + struct PartialCompletion { pub old_range: Range, pub new_text: String, - pub label: Option, + pub language: Option>, pub lsp_completion: lsp::CompletionItem, } @@ -3656,15 +3656,10 @@ impl Project { } }; - let label = match language.as_ref() { - Some(l) => Some(l.label_for_completion(&lsp_completion)), - None => None, - }; - let partial_completion = PartialCompletion { old_range, new_text, - label, + language: language.clone(), lsp_completion, }; @@ -3676,8 +3671,8 @@ impl Project { let mut result = Vec::new(); for pc in partial_completions.into_iter() { - let label = match pc.label { - Some(label) => label.await, + let label = match pc.language.as_ref() { + Some(l) => l.label_for_completion(&pc.lsp_completion).await, None => None, } .unwrap_or_else(|| { @@ -3716,10 +3711,11 @@ impl Project { }) .await; - let completions = Vec::new(); + let mut completions = Vec::new(); for completion in response.completions.into_iter() { - completions - .push(language::proto::deserialize_completion(completion, language).await); + completions.push( + language::proto::deserialize_completion(completion, language.clone()).await, + ); } completions.into_iter().collect() }) diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 838db5ef9d..ca72012094 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -1,5 +1,6 @@ use super::installation::{latest_github_release, GitHubLspBinaryVersion}; use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; pub use language::*; @@ -23,21 +24,18 @@ impl super::LspAdapter for CLspAdapter { &self, http: Arc, ) -> Result> { - async move { - let release = latest_github_release("clangd/clangd", http).await?; - let asset_name = format!("clangd-mac-{}.zip", release.name); - let asset = release - .assets - .iter() - .find(|asset| asset.name == asset_name) - .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; - let version = GitHubLspBinaryVersion { - name: release.name, - url: asset.browser_download_url.clone(), - }; - Ok(Box::new(version) as Box<_>) - } - .boxed() + let release = latest_github_release("clangd/clangd", http).await?; + let asset_name = format!("clangd-mac-{}.zip", release.name); + let asset = release + .assets + .iter() + .find(|asset| asset.name == asset_name) + .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; + let version = GitHubLspBinaryVersion { + name: release.name, + url: asset.browser_download_url.clone(), + }; + Ok(Box::new(version) as Box<_>) } async fn fetch_server_binary( @@ -47,54 +45,51 @@ impl super::LspAdapter for CLspAdapter { container_dir: PathBuf, ) -> Result { let version = version.downcast::().unwrap(); - async move { - let zip_path = container_dir.join(format!("clangd_{}.zip", version.name)); - let version_dir = container_dir.join(format!("clangd_{}", version.name)); - let binary_path = version_dir.join("bin/clangd"); + let zip_path = container_dir.join(format!("clangd_{}.zip", version.name)); + let version_dir = container_dir.join(format!("clangd_{}", version.name)); + let binary_path = version_dir.join("bin/clangd"); - if fs::metadata(&binary_path).await.is_err() { - let mut response = http - .get(&version.url, Default::default(), true) - .await - .context("error downloading release")?; - let mut file = File::create(&zip_path).await?; - if !response.status().is_success() { - Err(anyhow!( - "download failed with status {}", - response.status().to_string() - ))?; - } - futures::io::copy(response.body_mut(), &mut file).await?; + if fs::metadata(&binary_path).await.is_err() { + let mut response = http + .get(&version.url, Default::default(), true) + .await + .context("error downloading release")?; + let mut file = File::create(&zip_path).await?; + if !response.status().is_success() { + Err(anyhow!( + "download failed with status {}", + response.status().to_string() + ))?; + } + futures::io::copy(response.body_mut(), &mut file).await?; - let unzip_status = smol::process::Command::new("unzip") - .current_dir(&container_dir) - .arg(&zip_path) - .output() - .await? - .status; - if !unzip_status.success() { - Err(anyhow!("failed to unzip clangd archive"))?; - } + let unzip_status = smol::process::Command::new("unzip") + .current_dir(&container_dir) + .arg(&zip_path) + .output() + .await? + .status; + if !unzip_status.success() { + Err(anyhow!("failed to unzip clangd archive"))?; + } - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != version_dir { - fs::remove_dir_all(&entry_path).await.log_err(); - } + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); } } } } - - Ok(binary_path) } - .boxed() + + Ok(binary_path) } async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { - async move { + (|| async move { let mut last_clangd_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -113,9 +108,9 @@ impl super::LspAdapter for CLspAdapter { clangd_dir )) } - } + })() + .await .log_err() - .boxed() } async fn label_for_completion( diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index eb88d7f887..6445036a75 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -1,5 +1,6 @@ use super::installation::latest_github_release; use anyhow::{anyhow, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; pub use language::*; @@ -36,18 +37,15 @@ impl super::LspAdapter for GoLspAdapter { &self, http: Arc, ) -> Result> { - async move { - let release = latest_github_release("golang/tools", http).await?; - let version: Option = release.name.strip_prefix("gopls/v").map(str::to_string); - if version.is_none() { - log::warn!( - "couldn't infer gopls version from github release name '{}'", - release.name - ); - } - Ok(Box::new(version) as Box<_>) + let release = latest_github_release("golang/tools", http).await?; + let version: Option = release.name.strip_prefix("gopls/v").map(str::to_string); + if version.is_none() { + log::warn!( + "couldn't infer gopls version from github release name '{}'", + release.name + ); } - .boxed() + Ok(Box::new(version) as Box<_>) } async fn fetch_server_binary( @@ -59,65 +57,62 @@ impl super::LspAdapter for GoLspAdapter { let version = version.downcast::>().unwrap(); let this = *self; - async move { - if let Some(version) = *version { - let binary_path = container_dir.join(&format!("gopls_{version}")); - if let Ok(metadata) = fs::metadata(&binary_path).await { - if metadata.is_file() { - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != binary_path - && entry.file_name() != "gobin" - { - fs::remove_file(&entry_path).await.log_err(); - } + if let Some(version) = *version { + let binary_path = container_dir.join(&format!("gopls_{version}")); + if let Ok(metadata) = fs::metadata(&binary_path).await { + if metadata.is_file() { + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != binary_path + && entry.file_name() != "gobin" + { + fs::remove_file(&entry_path).await.log_err(); } } } - - return Ok(binary_path.to_path_buf()); } + + return Ok(binary_path.to_path_buf()); } - } else if let Some(path) = this.cached_server_binary(container_dir.clone()).await { - return Ok(path.to_path_buf()); } - - let gobin_dir = container_dir.join("gobin"); - fs::create_dir_all(&gobin_dir).await?; - let install_output = process::Command::new("go") - .env("GO111MODULE", "on") - .env("GOBIN", &gobin_dir) - .args(["install", "golang.org/x/tools/gopls@latest"]) - .output() - .await?; - if !install_output.status.success() { - Err(anyhow!("failed to install gopls. Is go installed?"))?; - } - - let installed_binary_path = gobin_dir.join("gopls"); - let version_output = process::Command::new(&installed_binary_path) - .arg("version") - .output() - .await - .map_err(|e| anyhow!("failed to run installed gopls binary {:?}", e))?; - let version_stdout = str::from_utf8(&version_output.stdout) - .map_err(|_| anyhow!("gopls version produced invalid utf8"))?; - let version = GOPLS_VERSION_REGEX - .find(version_stdout) - .ok_or_else(|| anyhow!("failed to parse gopls version output"))? - .as_str(); - let binary_path = container_dir.join(&format!("gopls_{version}")); - fs::rename(&installed_binary_path, &binary_path).await?; - - Ok(binary_path.to_path_buf()) + } else if let Some(path) = this.cached_server_binary(container_dir.clone()).await { + return Ok(path.to_path_buf()); } - .boxed() + + let gobin_dir = container_dir.join("gobin"); + fs::create_dir_all(&gobin_dir).await?; + let install_output = process::Command::new("go") + .env("GO111MODULE", "on") + .env("GOBIN", &gobin_dir) + .args(["install", "golang.org/x/tools/gopls@latest"]) + .output() + .await?; + if !install_output.status.success() { + Err(anyhow!("failed to install gopls. Is go installed?"))?; + } + + let installed_binary_path = gobin_dir.join("gopls"); + let version_output = process::Command::new(&installed_binary_path) + .arg("version") + .output() + .await + .map_err(|e| anyhow!("failed to run installed gopls binary {:?}", e))?; + let version_stdout = str::from_utf8(&version_output.stdout) + .map_err(|_| anyhow!("gopls version produced invalid utf8"))?; + let version = GOPLS_VERSION_REGEX + .find(version_stdout) + .ok_or_else(|| anyhow!("failed to parse gopls version output"))? + .as_str(); + let binary_path = container_dir.join(&format!("gopls_{version}")); + fs::rename(&installed_binary_path, &binary_path).await?; + + Ok(binary_path.to_path_buf()) } async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { - async move { + (|| async move { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -137,9 +132,9 @@ impl super::LspAdapter for GoLspAdapter { } else { Err(anyhow!("no cached binary")) } - } + })() + .await .log_err() - .boxed() } async fn label_for_completion( @@ -345,12 +340,12 @@ mod tests { let highlight_field = grammar.highlight_id_for_name("property").unwrap(); assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "Hello".to_string(), detail: Some("func(a B) c.D".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "Hello(a B) c.D".to_string(), filter_range: 0..5, @@ -364,12 +359,12 @@ mod tests { // Nested methods assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::METHOD), label: "one.two.Three".to_string(), detail: Some("func() [3]interface{}".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "one.two.Three() [3]interface{}".to_string(), filter_range: 0..13, @@ -383,12 +378,12 @@ mod tests { // Nested fields assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FIELD), label: "two.Three".to_string(), detail: Some("a.Bcd".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "two.Three a.Bcd".to_string(), filter_range: 0..9, diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 6d05f0a63e..04204dc665 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -1,5 +1,6 @@ use super::installation::{npm_install_packages, npm_package_latest_version}; use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; use language::{LanguageServerName, LspAdapter}; @@ -33,10 +34,7 @@ impl LspAdapter for JsonLspAdapter { &self, _: Arc, ) -> Result> { - async move { - Ok(Box::new(npm_package_latest_version("vscode-json-languageserver").await?) as Box<_>) - } - .boxed() + Ok(Box::new(npm_package_latest_version("vscode-json-languageserver").await?) as Box<_>) } async fn fetch_server_binary( @@ -46,39 +44,36 @@ impl LspAdapter for JsonLspAdapter { container_dir: PathBuf, ) -> Result { let version = version.downcast::().unwrap(); - async move { - let version_dir = container_dir.join(version.as_str()); - fs::create_dir_all(&version_dir) - .await - .context("failed to create version directory")?; - let binary_path = version_dir.join(Self::BIN_PATH); + let version_dir = container_dir.join(version.as_str()); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); - if fs::metadata(&binary_path).await.is_err() { - npm_install_packages( - [("vscode-json-languageserver", version.as_str())], - &version_dir, - ) - .await?; + if fs::metadata(&binary_path).await.is_err() { + npm_install_packages( + [("vscode-json-languageserver", version.as_str())], + &version_dir, + ) + .await?; - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != version_dir { - fs::remove_dir_all(&entry_path).await.log_err(); - } + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); } } } } - - Ok(binary_path) } - .boxed() + + Ok(binary_path) } async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { - async move { + (|| async move { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -97,9 +92,9 @@ impl LspAdapter for JsonLspAdapter { last_version_dir )) } - } + })() + .await .log_err() - .boxed() } async fn initialization_options(&self) -> Option { diff --git a/crates/zed/src/languages/language_plugin.rs b/crates/zed/src/languages/language_plugin.rs index 42693c2c32..77df85508e 100644 --- a/crates/zed/src/languages/language_plugin.rs +++ b/crates/zed/src/languages/language_plugin.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::lock::Mutex; use futures::Future; @@ -85,35 +86,26 @@ struct Versions { // I wish there was high-level instrumentation for this... // - it's totally a deadlock, the proof is in the pudding -// macro_rules! call_block { -// ($self:ident, $name:expr, $arg:expr) => { -// $self.executor.block(async { -// dbg!("starting to block on something"); -// let locked = $self.runtime.lock(); -// dbg!("locked runtime"); -// // TODO: No blocking calls! -// let mut awaited = locked.await; -// dbg!("awaited lock"); -// let called = awaited.call($name, $arg); -// dbg!("called function"); -// let result = called.await; -// dbg!("awaited result"); -// result -// }) -// }; -// } - -// TODO: convert to async trait - #[async_trait] impl LspAdapter for PluginLspAdapter { async fn name(&self) -> LanguageServerName { - let name: String = call_block!(self, &self.name, ()).unwrap(); + let name: String = self + .runtime + .lock() + .await + .call(&self.name, ()) + .await + .unwrap(); LanguageServerName(name.into()) } async fn server_args<'a>(&'a self) -> Vec { - call_block!(self, &self.server_args, ()).unwrap() + self.runtime + .lock() + .await + .call(&self.server_args, ()) + .await + .unwrap() } async fn fetch_latest_server_version( @@ -133,15 +125,15 @@ impl LspAdapter for PluginLspAdapter { .ok_or_else(|| anyhow!("Could not fetch latest server version")) .map(|v| Box::new(v) as Box<_>) }) - .boxed() + .await } - fn fetch_server_binary( + async fn fetch_server_binary( &self, version: Box, _: Arc, container_dir: PathBuf, - ) -> BoxFuture<'static, Result> { + ) -> Result { let version = *version.downcast::().unwrap(); let runtime = self.runtime.clone(); let function = self.fetch_server_binary; @@ -154,10 +146,10 @@ impl LspAdapter for PluginLspAdapter { runtime.remove_resource(handle)?; result.map_err(|e| anyhow!("{}", e)) }) - .boxed() + .await } - fn cached_server_binary(&self, container_dir: PathBuf) -> BoxFuture<'static, Option> { + async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { let runtime = self.runtime.clone(); let function = self.cached_server_binary; @@ -169,10 +161,10 @@ impl LspAdapter for PluginLspAdapter { runtime.remove_resource(handle).ok()?; result }) - .boxed() + .await } - fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} + // async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} // fn label_for_completion( // &self, @@ -193,8 +185,14 @@ impl LspAdapter for PluginLspAdapter { // }) // } - fn initialization_options(&self) -> Option { - let string: String = call_block!(self, &self.initialization_options, ()).log_err()?; + async fn initialization_options(&self) -> Option { + let string: String = self + .runtime + .lock() + .await + .call(&self.initialization_options, ()) + .await + .log_err()?; serde_json::from_str(&string).ok() } diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index d5f857ae05..475856b47d 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -1,5 +1,6 @@ use super::installation::{npm_install_packages, npm_package_latest_version}; use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; use language::{LanguageServerName, LspAdapter}; @@ -31,7 +32,7 @@ impl LspAdapter for PythonLspAdapter { &self, _: Arc, ) -> Result> { - async move { Ok(Box::new(npm_package_latest_version("pyright").await?) as Box<_>) }.boxed() + Ok(Box::new(npm_package_latest_version("pyright").await?) as Box<_>) } async fn fetch_server_binary( @@ -41,35 +42,32 @@ impl LspAdapter for PythonLspAdapter { container_dir: PathBuf, ) -> Result { let version = version.downcast::().unwrap(); - async move { - let version_dir = container_dir.join(version.as_str()); - fs::create_dir_all(&version_dir) - .await - .context("failed to create version directory")?; - let binary_path = version_dir.join(Self::BIN_PATH); + let version_dir = container_dir.join(version.as_str()); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); - if fs::metadata(&binary_path).await.is_err() { - npm_install_packages([("pyright", version.as_str())], &version_dir).await?; + if fs::metadata(&binary_path).await.is_err() { + npm_install_packages([("pyright", version.as_str())], &version_dir).await?; - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != version_dir { - fs::remove_dir_all(&entry_path).await.log_err(); - } + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); } } } } - - Ok(binary_path) } - .boxed() + + Ok(binary_path) } async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { - async move { + (|| async move { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -88,9 +86,9 @@ impl LspAdapter for PythonLspAdapter { last_version_dir )) } - } + })() + .await .log_err() - .boxed() } async fn label_for_completion( diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index c82fc896eb..bba5b526c9 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -1,6 +1,7 @@ use super::installation::{latest_github_release, GitHubLspBinaryVersion}; use anyhow::{anyhow, Result}; use async_compression::futures::bufread::GzipDecoder; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, io::BufReader, FutureExt, StreamExt}; pub use language::*; @@ -19,6 +20,7 @@ use util::{ResultExt, TryFutureExt}; pub struct RustLspAdapter; +#[async_trait] impl LspAdapter for RustLspAdapter { async fn name(&self) -> LanguageServerName { LanguageServerName("rust-analyzer".into()) @@ -28,21 +30,18 @@ impl LspAdapter for RustLspAdapter { &self, http: Arc, ) -> Result> { - async move { - let release = latest_github_release("rust-analyzer/rust-analyzer", http).await?; - let asset_name = format!("rust-analyzer-{}-apple-darwin.gz", consts::ARCH); - let asset = release - .assets - .iter() - .find(|asset| asset.name == asset_name) - .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; - let version = GitHubLspBinaryVersion { - name: release.name, - url: asset.browser_download_url.clone(), - }; - Ok(Box::new(version) as Box<_>) - } - .boxed() + let release = latest_github_release("rust-analyzer/rust-analyzer", http).await?; + let asset_name = format!("rust-analyzer-{}-apple-darwin.gz", consts::ARCH); + let asset = release + .assets + .iter() + .find(|asset| asset.name == asset_name) + .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; + let version = GitHubLspBinaryVersion { + name: release.name, + url: asset.browser_download_url.clone(), + }; + Ok(Box::new(version) as Box<_>) } async fn fetch_server_binary( @@ -51,55 +50,49 @@ impl LspAdapter for RustLspAdapter { http: Arc, container_dir: PathBuf, ) -> Result { - async move { - let version = version.downcast::().unwrap(); - let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name)); + let version = version.downcast::().unwrap(); + let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name)); - if fs::metadata(&destination_path).await.is_err() { - let mut response = http - .get(&version.url, Default::default(), true) - .await - .map_err(|err| anyhow!("error downloading release: {}", err))?; - let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); - let mut file = File::create(&destination_path).await?; - futures::io::copy(decompressed_bytes, &mut file).await?; - fs::set_permissions( - &destination_path, - ::from_mode(0o755), - ) - .await?; + if fs::metadata(&destination_path).await.is_err() { + let mut response = http + .get(&version.url, Default::default(), true) + .await + .map_err(|err| anyhow!("error downloading release: {}", err))?; + let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); + let mut file = File::create(&destination_path).await?; + futures::io::copy(decompressed_bytes, &mut file).await?; + fs::set_permissions( + &destination_path, + ::from_mode(0o755), + ) + .await?; - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != destination_path { - fs::remove_file(&entry_path).await.log_err(); - } + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != destination_path { + fs::remove_file(&entry_path).await.log_err(); } } } } - - Ok(destination_path) } - .boxed() + + Ok(destination_path) } - async fn cached_server_binary( - &self, - container_dir: PathBuf, - ) -> BoxFuture<'static, Option> { - async move { + async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { + (|| async move { let mut last = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { last = Some(entry?.path()); } last.ok_or_else(|| anyhow!("no cached binary")) - } + })() + .await .log_err() - .boxed() } async fn disk_based_diagnostic_sources(&self) -> Vec { @@ -337,12 +330,12 @@ mod tests { let highlight_field = grammar.highlight_id_for_name("property").unwrap(); assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "hello(…)".to_string(), detail: Some("fn(&mut Option) -> Vec".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "hello(&mut Option) -> Vec".to_string(), filter_range: 0..5, @@ -358,12 +351,12 @@ mod tests { ); assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FIELD), label: "len".to_string(), detail: Some("usize".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "len: usize".to_string(), filter_range: 0..3, @@ -372,12 +365,12 @@ mod tests { ); assert_eq!( - language.label_for_completion(&lsp::CompletionItem { + smol::block_on(language.label_for_completion(&lsp::CompletionItem { kind: Some(lsp::CompletionItemKind::FUNCTION), label: "hello(…)".to_string(), detail: Some("fn(&mut Option) -> Vec".to_string()), ..Default::default() - }), + })), Some(CodeLabel { text: "hello(&mut Option) -> Vec".to_string(), filter_range: 0..5, @@ -415,7 +408,7 @@ mod tests { let highlight_keyword = grammar.highlight_id_for_name("keyword").unwrap(); assert_eq!( - language.label_for_symbol("hello", lsp::SymbolKind::FUNCTION), + smol::block_on(language.label_for_symbol("hello", lsp::SymbolKind::FUNCTION)), Some(CodeLabel { text: "fn hello".to_string(), filter_range: 3..8, @@ -424,7 +417,7 @@ mod tests { ); assert_eq!( - language.label_for_symbol("World", lsp::SymbolKind::TYPE_PARAMETER), + smol::block_on(language.label_for_symbol("World", lsp::SymbolKind::TYPE_PARAMETER)), Some(CodeLabel { text: "type World".to_string(), filter_range: 5..10, diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 0bd5ad44a9..890317948d 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -1,5 +1,6 @@ use super::installation::{npm_install_packages, npm_package_latest_version}; use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; use language::{LanguageServerName, LspAdapter}; @@ -40,13 +41,10 @@ impl LspAdapter for TypeScriptLspAdapter { &self, _: Arc, ) -> Result> { - async move { - Ok(Box::new(Versions { - typescript_version: npm_package_latest_version("typescript").await?, - server_version: npm_package_latest_version("typescript-language-server").await?, - }) as Box<_>) - } - .boxed() + Ok(Box::new(Versions { + typescript_version: npm_package_latest_version("typescript").await?, + server_version: npm_package_latest_version("typescript-language-server").await?, + }) as Box<_>) } async fn fetch_server_binary( @@ -56,48 +54,45 @@ impl LspAdapter for TypeScriptLspAdapter { container_dir: PathBuf, ) -> Result { let versions = versions.downcast::().unwrap(); - async move { - let version_dir = container_dir.join(&format!( - "typescript-{}:server-{}", - versions.typescript_version, versions.server_version - )); - fs::create_dir_all(&version_dir) - .await - .context("failed to create version directory")?; - let binary_path = version_dir.join(Self::BIN_PATH); + let version_dir = container_dir.join(&format!( + "typescript-{}:server-{}", + versions.typescript_version, versions.server_version + )); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); - if fs::metadata(&binary_path).await.is_err() { - npm_install_packages( - [ - ("typescript", versions.typescript_version.as_str()), - ( - "typescript-language-server", - &versions.server_version.as_str(), - ), - ], - &version_dir, - ) - .await?; + if fs::metadata(&binary_path).await.is_err() { + npm_install_packages( + [ + ("typescript", versions.typescript_version.as_str()), + ( + "typescript-language-server", + &versions.server_version.as_str(), + ), + ], + &version_dir, + ) + .await?; - if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { - while let Some(entry) = entries.next().await { - if let Some(entry) = entry.log_err() { - let entry_path = entry.path(); - if entry_path.as_path() != version_dir { - fs::remove_dir_all(&entry_path).await.log_err(); - } + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); } } } } - - Ok(binary_path) } - .boxed() + + Ok(binary_path) } async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { - async move { + (|| async move { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -116,9 +111,9 @@ impl LspAdapter for TypeScriptLspAdapter { last_version_dir )) } - } + })() + .await .log_err() - .boxed() } async fn label_for_completion(