Route some more information for reinstall after startup failure

Julia created

Doesn't actually reinstall on that particular failure due to wrong
variant in hashmap

Change summary

crates/language/src/language.rs | 101 ++++++++++++++++++++--------------
crates/lsp/src/lsp.rs           |  10 +-
crates/project/src/project.rs   |  94 +++++++++++++++++++++-----------
3 files changed, 126 insertions(+), 79 deletions(-)

Detailed changes

crates/language/src/language.rs 🔗

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

crates/lsp/src/lsp.rs 🔗

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

crates/project/src/project.rs 🔗

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