Detailed changes
@@ -130,12 +130,20 @@ impl CachedLspAdapter {
self.adapter.fetch_latest_server_version(delegate).await
}
- pub fn will_fetch_server_binary(
+ pub fn will_fetch_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
- self.adapter.will_fetch_server_binary(delegate, cx)
+ self.adapter.will_fetch_server(delegate, cx)
+ }
+
+ pub fn will_start_server(
+ &self,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ cx: &mut AsyncAppContext,
+ ) -> Option<Task<Result<()>>> {
+ self.adapter.will_start_server(delegate, cx)
}
pub async fn fetch_server_binary(
@@ -212,7 +220,15 @@ pub trait LspAdapter: 'static + Send + Sync {
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>>;
- fn will_fetch_server_binary(
+ fn will_fetch_server(
+ &self,
+ _: &Arc<dyn LspAdapterDelegate>,
+ _: &mut AsyncAppContext,
+ ) -> Option<Task<Result<()>>> {
+ None
+ }
+
+ fn will_start_server(
&self,
_: &Arc<dyn LspAdapterDelegate>,
_: &mut AsyncAppContext,
@@ -891,7 +907,7 @@ impl LanguageRegistry {
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
let login_shell_env_loaded = self.login_shell_env_loaded.clone();
- let task = cx.spawn(|cx| async move {
+ let task = cx.spawn(|mut cx| async move {
login_shell_env_loaded.await;
let entry = this
@@ -903,7 +919,7 @@ impl LanguageRegistry {
get_binary(
adapter.clone(),
language.clone(),
- delegate,
+ delegate.clone(),
download_dir,
lsp_binary_statuses,
cx,
@@ -915,6 +931,10 @@ impl LanguageRegistry {
.clone();
let binary = entry.clone().map_err(|e| anyhow!(e)).await?;
+ if let Some(task) = adapter.will_start_server(&delegate, &mut cx) {
+ task.await?;
+ }
+
let server = lsp::LanguageServer::new(
server_id,
&binary.path,
@@ -996,7 +1016,7 @@ async fn get_binary(
.context("failed to create container directory")?;
}
- if let Some(task) = adapter.will_fetch_server_binary(&delegate, &mut cx) {
+ if let Some(task) = adapter.will_fetch_server(&delegate, &mut cx) {
task.await?;
}
@@ -1,10 +1,18 @@
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use futures::StreamExt;
+use gpui::{AsyncAppContext, Task};
pub use language::*;
use lsp::{CompletionItemKind, SymbolKind};
use smol::fs::{self, File};
-use std::{any::Any, path::PathBuf, sync::Arc};
+use std::{
+ any::Any,
+ path::PathBuf,
+ sync::{
+ atomic::{AtomicBool, Ordering::SeqCst},
+ Arc,
+ },
+};
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
@@ -19,6 +27,37 @@ impl LspAdapter for ElixirLspAdapter {
LanguageServerName("elixir-ls".into())
}
+ fn will_start_server(
+ &self,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ cx: &mut AsyncAppContext,
+ ) -> Option<Task<Result<()>>> {
+ static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
+
+ const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
+
+ let delegate = delegate.clone();
+ Some(cx.spawn(|mut cx| async move {
+ let elixir_output = smol::process::Command::new("elixir")
+ .args(["--version"])
+ .output()
+ .await;
+ if elixir_output.is_err() {
+ if DID_SHOW_NOTIFICATION
+ .compare_exchange(false, true, SeqCst, SeqCst)
+ .is_ok()
+ {
+ cx.update(|cx| {
+ delegate.show_notification(NOTIFICATION_MESSAGE, cx);
+ })
+ }
+ return Err(anyhow!("cannot run elixir-ls"));
+ }
+
+ Ok(())
+ }))
+ }
+
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
@@ -12,7 +12,10 @@ use std::{
ops::Range,
path::PathBuf,
str,
- sync::Arc,
+ sync::{
+ atomic::{AtomicBool, Ordering::SeqCst},
+ Arc,
+ },
};
use util::{fs::remove_matching, github::latest_github_release, ResultExt};
@@ -48,19 +51,29 @@ impl super::LspAdapter for GoLspAdapter {
Ok(Box::new(version) as Box<_>)
}
- fn will_fetch_server_binary(
+ fn will_fetch_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
+ static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
+
+ const NOTIFICATION_MESSAGE: &str =
+ "Could not install the Go language server `gopls`, because `go` was not found.";
+
let delegate = delegate.clone();
Some(cx.spawn(|mut cx| async move {
let install_output = process::Command::new("go").args(["version"]).output().await;
if install_output.is_err() {
- cx.update(|cx| {
- delegate
- .show_notification("go is not installed. gopls will not be available.", cx);
- })
+ if DID_SHOW_NOTIFICATION
+ .compare_exchange(false, true, SeqCst, SeqCst)
+ .is_ok()
+ {
+ cx.update(|cx| {
+ delegate.show_notification(NOTIFICATION_MESSAGE, cx);
+ })
+ }
+ return Err(anyhow!("cannot install gopls"));
}
Ok(())
}))