diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 2ed896e03f..28cd1cd5b1 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -20,7 +20,7 @@ use futures::{ use gpui::{executor::Background, AppContext, Task}; use highlight_map::HighlightMap; use lazy_static::lazy_static; -use lsp::{CodeActionKind, LanguageServer, LanguageServerBinaries, LanguageServerBinary}; +use lsp::{CodeActionKind, LanguageServerBinaries, LanguageServerBinary}; use parking_lot::{Mutex, RwLock}; use postage::watch; use regex::Regex; @@ -35,13 +35,11 @@ use std::{ mem, ops::{Not, Range}, path::{Path, PathBuf}, - process::Stdio, str, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, - time::Duration, }; use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; @@ -860,7 +858,7 @@ impl LanguageRegistry { let download_dir = self .language_server_download_dir .clone() - .ok_or_else(|| anyhow!("language server download directory has not been assigned")) + .ok_or_else(|| anyhow!("language server download directory has not been assigned before starting server")) .log_err()?; let this = self.clone(); let language = language.clone(); @@ -913,54 +911,26 @@ impl LanguageRegistry { self.lsp_binary_statuses_rx.clone() } - pub async fn check_errored_lsp_installation( + pub fn delete_server_container( &self, - language_server: Arc, + adapter: Arc, cx: &mut AppContext, - ) { - // Check if child process is running - if !language_server.is_dead() { - return; - } + ) -> Task<()> { + let mut lock = self.lsp_binary_paths.lock(); + lock.remove(&adapter.name); - // If not, get check binary - let test_binary = match language_server.test_installation_binary() { - Some(test_binary) => test_binary.clone(), - None => return, - }; + let download_dir = self + .language_server_download_dir + .clone() + .expect("language server download directory has not been assigned before deleting server container"); - // Run - const PROCESS_TIMEOUT: Duration = Duration::from_secs(5); - let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse(); - - let mut errored = false; - let result = smol::process::Command::new(&test_binary.path) - .current_dir(&test_binary.path) - .args(test_binary.arguments) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .kill_on_drop(true) - .spawn(); - - if let Ok(mut process) = result { - futures::select! { - _ = process.status().fuse() => {} - _ = timeout => errored = true, - } - } else { - errored = true; - } - - dbg!(errored); - - // If failure clear container dir - - // Prompt binary retrieval - - // Start language server - - // Update project server state + cx.spawn(|_| async move { + let container_dir = download_dir.join(adapter.name.0.as_ref()); + smol::fs::remove_dir_all(container_dir) + .await + .context("server container removal") + .log_err(); + }) } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5c9b79434a..e131bbb746 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2461,6 +2461,54 @@ impl Project { } } + fn reinstall_language_server( + &mut self, + server_id: LanguageServerId, + cx: &mut ModelContext, + ) -> Option> { + let (adapter, language, server) = match self.language_servers.remove(&server_id) { + Some(LanguageServerState::Running { + adapter, + language, + server, + .. + }) => (adapter.clone(), language.clone(), server), + + _ => return None, + }; + + Some(cx.spawn(move |this, mut cx| async move { + if let Some(task) = server.shutdown() { + task.await; + } + + // TODO: This is race-safe with regards to preventing new instances from + // starting while deleting, but existing instances in other projects are going + // to be very confused and messed up + this.update(&mut cx, |this, cx| { + this.languages.delete_server_container(adapter.clone(), cx) + }) + .await; + + this.update(&mut cx, |this, mut cx| { + for worktree in &this.worktrees { + let root_path = match worktree.upgrade(cx) { + Some(worktree) => worktree.read(cx).abs_path(), + None => continue, + }; + + this.languages.start_language_server( + language.clone(), + adapter.clone(), + root_path, + this.client.http_client(), + &mut cx, + ); + } + }) + })) + } + async fn setup_and_insert_language_server( this: WeakModelHandle, initialization_options: Option, @@ -2950,8 +2998,54 @@ impl Project { language_server: Arc, cx: &mut ModelContext, ) { - self.languages - .check_errored_lsp_installation(language_server, cx); + cx.spawn(|this, mut cx| async move { + if !language_server.is_dead() { + return; + } + + let server_id = language_server.server_id(); + let test_binary = match language_server.test_installation_binary() { + Some(test_binary) => test_binary.clone(), + None => return, + }; + + const PROCESS_TIMEOUT: Duration = Duration::from_secs(5); + let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse(); + + let mut errored = false; + let result = smol::process::Command::new(&test_binary.path) + .current_dir(&test_binary.path) + .args(test_binary.arguments) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .kill_on_drop(true) + .spawn(); + + if let Ok(mut process) = result { + futures::select! { + status = process.status().fuse() => match status { + Ok(status) => errored = !status.success(), + Err(_) => errored = true, + }, + + _ = timeout => {} + } + } else { + errored = true; + } + + if errored { + let task = this.update(&mut cx, move |this, mut cx| { + this.reinstall_language_server(server_id, &mut cx) + }); + + if let Some(task) = task { + task.await; + } + } + }) + .detach(); } fn on_lsp_progress( diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index bfb25f55a0..9d4e1802d5 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -109,6 +109,10 @@ impl super::LspAdapter for CLspAdapter { .await .log_err() } + + fn installation_test_binary(&self, container_dir: PathBuf) -> LanguageServerBinary { + unimplemented!(); + } async fn label_for_completion( &self,