Route some more information for reinstall after startup failure

Doesn't actually reinstall on that particular failure due to wrong
variant in hashmap
This commit is contained in:
Julia 2023-06-19 15:18:12 -04:00
parent abe5ecc5ec
commit da2ee55013
3 changed files with 125 additions and 78 deletions

View file

@ -141,7 +141,7 @@ impl CachedLspAdapter {
self.adapter.cached_server_binary(container_dir).await self.adapter.cached_server_binary(container_dir).await
} }
async fn installation_test_binary( pub async fn installation_test_binary(
&self, &self,
container_dir: PathBuf, container_dir: PathBuf,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
@ -544,6 +544,7 @@ struct LanguageRegistryState {
pub struct PendingLanguageServer { pub struct PendingLanguageServer {
pub server_id: LanguageServerId, pub server_id: LanguageServerId,
pub task: Task<Result<lsp::LanguageServer>>, pub task: Task<Result<lsp::LanguageServer>>,
pub container_dir: Option<Arc<Path>>,
} }
impl LanguageRegistry { impl LanguageRegistry {
@ -824,8 +825,8 @@ impl LanguageRegistry {
cx: &mut AppContext, cx: &mut AppContext,
) -> Option<PendingLanguageServer> { ) -> Option<PendingLanguageServer> {
let server_id = self.state.write().next_language_server_id(); let server_id = self.state.write().next_language_server_id();
log::info!( println!(
"starting language server name:{}, path:{root_path:?}, id:{server_id}", "starting language server {:?}, path: {root_path:?}, id: {server_id}",
adapter.name.0 adapter.name.0
); );
@ -858,7 +859,11 @@ impl LanguageRegistry {
Ok(server) Ok(server)
}); });
return Some(PendingLanguageServer { server_id, task }); return Some(PendingLanguageServer {
server_id,
task,
container_dir: None,
});
} }
let download_dir = self let download_dir = self
@ -869,46 +874,54 @@ impl LanguageRegistry {
let this = self.clone(); let this = self.clone();
let language = language.clone(); let language = language.clone();
let http_client = http_client.clone(); let http_client = http_client.clone();
let download_dir = download_dir.clone(); let container_dir: Arc<Path> = Arc::from(download_dir.join(adapter.name.0.as_ref()));
let root_path = root_path.clone(); let root_path = root_path.clone();
let adapter = adapter.clone(); let adapter = adapter.clone();
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone(); let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
let login_shell_env_loaded = self.login_shell_env_loaded.clone(); let login_shell_env_loaded = self.login_shell_env_loaded.clone();
let task = cx.spawn(|cx| async move { let task = {
login_shell_env_loaded.await; let container_dir = container_dir.clone();
cx.spawn(|cx| async move {
login_shell_env_loaded.await;
let mut lock = this.lsp_binary_paths.lock(); let mut lock = this.lsp_binary_paths.lock();
let entry = lock let entry = lock
.entry(adapter.name.clone()) .entry(adapter.name.clone())
.or_insert_with(|| { .or_insert_with(|| {
get_binary( get_binaries(
adapter.clone(), adapter.clone(),
language.clone(), language.clone(),
http_client, http_client,
download_dir, container_dir,
lsp_binary_statuses, lsp_binary_statuses,
) )
.map_err(Arc::new) .map_err(Arc::new)
.boxed() .boxed()
.shared() .shared()
}) })
.clone(); .clone();
drop(lock); drop(lock);
let binary = entry.clone().map_err(|e| anyhow!(e)).await?; let binaries = entry.clone().map_err(|e| anyhow!(e)).await?;
let server = lsp::LanguageServer::new( println!("starting server");
server_id, let server = lsp::LanguageServer::new(
binary, server_id,
&root_path, binaries,
adapter.code_action_kinds(), &root_path,
cx, adapter.code_action_kinds(),
)?; cx,
)?;
Ok(server) Ok(server)
}); })
};
Some(PendingLanguageServer { server_id, task }) Some(PendingLanguageServer {
server_id,
task,
container_dir: Some(container_dir),
})
} }
pub fn language_server_binary_statuses( pub fn language_server_binary_statuses(
@ -922,6 +935,7 @@ impl LanguageRegistry {
adapter: Arc<CachedLspAdapter>, adapter: Arc<CachedLspAdapter>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task<()> { ) -> Task<()> {
println!("deleting server container");
let mut lock = self.lsp_binary_paths.lock(); let mut lock = self.lsp_binary_paths.lock();
lock.remove(&adapter.name); lock.remove(&adapter.name);
@ -984,20 +998,20 @@ impl Default for LanguageRegistry {
} }
} }
async fn get_binary( async fn get_binaries(
adapter: Arc<CachedLspAdapter>, adapter: Arc<CachedLspAdapter>,
language: Arc<Language>, language: Arc<Language>,
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
download_dir: Arc<Path>, container_dir: Arc<Path>,
statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>, statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
) -> Result<LanguageServerBinaries> { ) -> Result<LanguageServerBinaries> {
let container_dir = download_dir.join(adapter.name.0.as_ref());
if !container_dir.exists() { if !container_dir.exists() {
smol::fs::create_dir_all(&container_dir) smol::fs::create_dir_all(&container_dir)
.await .await
.context("failed to create container directory")?; .context("failed to create container directory")?;
} }
println!("fetching binary");
let binary = fetch_latest_binary( let binary = fetch_latest_binary(
adapter.clone(), adapter.clone(),
language.clone(), language.clone(),
@ -1008,11 +1022,16 @@ async fn get_binary(
.await; .await;
if let Err(error) = binary.as_ref() { if let Err(error) = binary.as_ref() {
if let Some(binary) = adapter.cached_server_binary(container_dir.clone()).await { if let Some(binary) = adapter
.cached_server_binary(container_dir.to_path_buf())
.await
{
statuses statuses
.broadcast((language.clone(), LanguageServerBinaryStatus::Cached)) .broadcast((language.clone(), LanguageServerBinaryStatus::Cached))
.await?; .await?;
let installation_test_binary = adapter.installation_test_binary(container_dir).await; let installation_test_binary = adapter
.installation_test_binary(container_dir.to_path_buf())
.await;
return Ok(LanguageServerBinaries { return Ok(LanguageServerBinaries {
binary, binary,
installation_test_binary, installation_test_binary,

View file

@ -65,7 +65,7 @@ pub struct LanguageServer {
output_done_rx: Mutex<Option<barrier::Receiver>>, output_done_rx: Mutex<Option<barrier::Receiver>>,
root_path: PathBuf, root_path: PathBuf,
server: Option<Mutex<Child>>, server: Option<Mutex<Child>>,
test_installation_binary: Option<LanguageServerBinary>, installation_test_binary: Option<LanguageServerBinary>,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -190,7 +190,7 @@ impl LanguageServer {
stdin: Stdin, stdin: Stdin,
stdout: Stdout, stdout: Stdout,
server: Option<Child>, server: Option<Child>,
test_installation_binary: Option<LanguageServerBinary>, installation_test_binary: Option<LanguageServerBinary>,
root_path: &Path, root_path: &Path,
code_action_kinds: Option<Vec<CodeActionKind>>, code_action_kinds: Option<Vec<CodeActionKind>>,
cx: AsyncAppContext, cx: AsyncAppContext,
@ -245,7 +245,7 @@ impl LanguageServer {
output_done_rx: Mutex::new(Some(output_done_rx)), output_done_rx: Mutex::new(Some(output_done_rx)),
root_path: root_path.to_path_buf(), root_path: root_path.to_path_buf(),
server: server.map(|server| Mutex::new(server)), server: server.map(|server| Mutex::new(server)),
test_installation_binary, installation_test_binary,
} }
} }
@ -262,8 +262,8 @@ impl LanguageServer {
} }
} }
pub fn test_installation_binary(&self) -> &Option<LanguageServerBinary> { pub fn installation_test_binary(&self) -> &Option<LanguageServerBinary> {
&self.test_installation_binary &self.installation_test_binary
} }
pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {

View file

@ -45,7 +45,7 @@ use language::{
use log::error; use log::error;
use lsp::{ use lsp::{
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
DocumentHighlightKind, LanguageServer, LanguageServerId, OneOf, DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf,
}; };
use lsp_command::*; use lsp_command::*;
use postage::watch; use postage::watch;
@ -2442,22 +2442,23 @@ impl Project {
} }
let server_id = pending_server.server_id; let server_id = pending_server.server_id;
let container_dir = pending_server.container_dir.clone();
let state = LanguageServerState::Starting({ let state = LanguageServerState::Starting({
let server_name = adapter.name.0.clone(); let server_name = adapter.name.0.clone();
let languages = self.languages.clone(); let languages = self.languages.clone();
let key = key.clone(); let key = key.clone();
cx.spawn_weak(|this, cx| async move { cx.spawn_weak(|this, mut cx| async move {
let result = Self::setup_and_insert_language_server( let result = Self::setup_and_insert_language_server(
this, this,
initialization_options, initialization_options,
pending_server, pending_server,
adapter, adapter.clone(),
languages, languages,
language, language,
server_id, server_id,
key, key,
cx, &mut cx,
) )
.await; .await;
@ -2465,8 +2466,24 @@ impl Project {
Ok(server) => Some(server), Ok(server) => Some(server),
Err(err) => { Err(err) => {
log::warn!("Error starting language server {:?}: {}", server_name, err); println!("failed to start language server {:?}: {}", server_name, err);
// TODO: Prompt installation validity check LSP ERROR
if let Some(this) = this.upgrade(&cx) {
if let Some(container_dir) = container_dir {
let installation_test_binary = adapter
.installation_test_binary(container_dir.to_path_buf())
.await;
this.update(&mut cx, |_, cx| {
Self::check_errored_server_id(
server_id,
installation_test_binary,
cx,
)
});
}
}
None None
} }
} }
@ -2482,6 +2499,7 @@ impl Project {
server_id: LanguageServerId, server_id: LanguageServerId,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Option<Task<()>> { ) -> Option<Task<()>> {
println!("starting to reinstall server");
let (adapter, language, server) = match self.language_servers.remove(&server_id) { let (adapter, language, server) = match self.language_servers.remove(&server_id) {
Some(LanguageServerState::Running { Some(LanguageServerState::Running {
adapter, adapter,
@ -2495,6 +2513,7 @@ impl Project {
Some(cx.spawn(move |this, mut cx| async move { Some(cx.spawn(move |this, mut cx| async move {
if let Some(task) = server.shutdown() { if let Some(task) = server.shutdown() {
println!("shutting down existing server");
task.await; task.await;
} }
@ -2516,6 +2535,7 @@ impl Project {
let worktree_id = worktree.id(); let worktree_id = worktree.id();
let root_path = worktree.abs_path(); let root_path = worktree.abs_path();
println!("prompting server start: {:?}", &adapter.name.0);
this.start_language_server( this.start_language_server(
worktree_id, worktree_id,
root_path, root_path,
@ -2537,7 +2557,7 @@ impl Project {
language: Arc<Language>, language: Arc<Language>,
server_id: LanguageServerId, server_id: LanguageServerId,
key: (WorktreeId, LanguageServerName), key: (WorktreeId, LanguageServerName),
mut cx: AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<Arc<LanguageServer>> { ) -> Result<Arc<LanguageServer>> {
let language_server = Self::setup_pending_language_server( let language_server = Self::setup_pending_language_server(
this, this,
@ -2546,16 +2566,16 @@ impl Project {
adapter.clone(), adapter.clone(),
languages, languages,
server_id, server_id,
&mut cx, cx,
) )
.await?; .await?;
let this = match this.upgrade(&mut cx) { let this = match this.upgrade(cx) {
Some(this) => this, Some(this) => this,
None => return Err(anyhow!("failed to upgrade project handle")), None => return Err(anyhow!("failed to upgrade project handle")),
}; };
this.update(&mut cx, |this, cx| { this.update(cx, |this, cx| {
this.insert_newly_running_language_server( this.insert_newly_running_language_server(
language, language,
adapter, adapter,
@ -3012,32 +3032,39 @@ impl Project {
.detach(); .detach();
} }
fn check_errored_lsp_installation( fn check_errored_language_server(
&self, &self,
language_server: Arc<LanguageServer>, language_server: Arc<LanguageServer>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
cx.spawn(|this, mut cx| async move { if !language_server.is_dead() {
if !language_server.is_dead() { return;
return; }
}
let server_id = language_server.server_id();
let server_id = language_server.server_id();
let installation_test_binary = language_server.installation_test_binary().clone();
Self::check_errored_server_id(server_id, installation_test_binary, cx);
}
fn check_errored_server_id(
server_id: LanguageServerId,
installation_test_binary: Option<LanguageServerBinary>,
cx: &mut ModelContext<Self>,
) {
cx.spawn(|this, mut cx| async move {
println!("About to spawn test binary");
// A lack of test binary counts as a failure // A lack of test binary counts as a failure
let process = language_server let process = installation_test_binary.and_then(|binary| {
.test_installation_binary() smol::process::Command::new(&binary.path)
.as_ref() .current_dir(&binary.path)
.and_then(|binary| { .args(binary.arguments)
smol::process::Command::new(&binary.path) .stdin(Stdio::piped())
.current_dir(&binary.path) .stdout(Stdio::piped())
.args(&binary.arguments) .stderr(Stdio::inherit())
.stdin(Stdio::piped()) .kill_on_drop(true)
.stdout(Stdio::piped()) .spawn()
.stderr(Stdio::inherit()) .ok()
.kill_on_drop(true) });
.spawn()
.ok()
});
const PROCESS_TIMEOUT: Duration = Duration::from_secs(5); const PROCESS_TIMEOUT: Duration = Duration::from_secs(5);
let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse(); let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse();
@ -3046,13 +3073,14 @@ impl Project {
if let Some(mut process) = process { if let Some(mut process) = process {
futures::select! { futures::select! {
status = process.status().fuse() => match status { status = process.status().fuse() => match status {
Ok(status) => errored = !status.success(), Ok(status) => errored = !dbg!(status.success()),
Err(_) => errored = true, Err(_) => errored = true,
}, },
_ = timeout => {} _ = timeout => { println!("test binary time-ed out"); }
} }
} else { } else {
println!("test binary failed to launch");
errored = true; errored = true;
} }
@ -3883,7 +3911,7 @@ impl Project {
); );
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.check_errored_lsp_installation(language_server.clone(), cx); this.check_errored_language_server(language_server.clone(), cx);
}); });
None None