Detailed changes
@@ -712,61 +712,64 @@ impl Project {
}
fn on_settings_changed(&mut self, cx: &mut ModelContext<'_, Self>) {
- let settings = cx.global::<Settings>();
- 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()));
- }
- }
- }
- }
- }
-
- 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);
- }
-
- cx.notify();
- });
- }))
+ // let settings = cx.global::<Settings>();
+ // 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()));
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ // 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);
+ // }
+
+ // cx.notify();
+ // });
+ // }))
+
+ // TODO(isaac): uncomment the above
+ todo!()
}
pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
@@ -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<F1, F2>
- where
- F1: Future<Output = LanguageServerName>,
- F2: Future<Output = Option<CodeLabel>>,
- {
+ struct PartialSymbol {
source_worktree_id: WorktreeId,
worktree_id: WorktreeId,
- language_server_name: F1,
+ adapter: Arc<dyn LspAdapter>,
path: PathBuf,
- label: Option<F2>,
+ language: Option<Arc<Language>>,
name: String,
kind: lsp::SymbolKind,
range: Range<PointUtf16>,
@@ -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<F: Future<Output = Option<CodeLabel>>> {
+ struct PartialCompletion {
pub old_range: Range<Anchor>,
pub new_text: String,
- pub label: Option<F>,
+ pub language: Option<Arc<Language>>,
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()
})
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
- 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<PathBuf> {
let version = version.downcast::<GitHubLspBinaryVersion>().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<PathBuf> {
- 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(
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
- async move {
- let release = latest_github_release("golang/tools", http).await?;
- let version: Option<String> = 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<String> = 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::<Option<String>>().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());
}
+ } 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 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?;
+ 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())
- }
- .boxed()
+ Ok(binary_path.to_path_buf())
}
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
- 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,
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Any + Send>> {
- 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<PathBuf> {
let version = version.downcast::<String>().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<PathBuf> {
- 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<serde_json::Value> {
@@ -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<String> {
- 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<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
- ) -> BoxFuture<'static, Result<PathBuf>> {
+ ) -> Result<PathBuf> {
let version = *version.downcast::<String>().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<PathBuf>> {
+ async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
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<serde_json::Value> {
- let string: String = call_block!(self, &self.initialization_options, ()).log_err()?;
+ async fn initialization_options(&self) -> Option<serde_json::Value> {
+ let string: String = self
+ .runtime
+ .lock()
+ .await
+ .call(&self.initialization_options, ())
+ .await
+ .log_err()?;
serde_json::from_str(&string).ok()
}
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Any + Send>> {
- 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<PathBuf> {
let version = version.downcast::<String>().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);
-
- 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();
- }
+ 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 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<PathBuf> {
- 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(
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
- 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<dyn HttpClient>,
container_dir: PathBuf,
) -> Result<PathBuf> {
- async move {
- let version = version.downcast::<GitHubLspBinaryVersion>().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,
- <fs::Permissions as fs::unix::PermissionsExt>::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();
- }
+ let version = version.downcast::<GitHubLspBinaryVersion>().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,
+ <fs::Permissions as fs::unix::PermissionsExt>::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();
}
}
}
}
-
- Ok(destination_path)
}
- .boxed()
+
+ Ok(destination_path)
}
- async fn cached_server_binary(
- &self,
- container_dir: PathBuf,
- ) -> BoxFuture<'static, Option<PathBuf>> {
- async move {
+ async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
+ (|| 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<String> {
@@ -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<T>) -> Vec<T>".to_string()),
..Default::default()
- }),
+ })),
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T>".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<T>) -> Vec<T>".to_string()),
..Default::default()
- }),
+ })),
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T>".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,
@@ -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<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
- 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<PathBuf> {
let versions = versions.downcast::<Versions>().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);
-
- 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();
- }
+ 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 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<PathBuf> {
- 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(