From 5aed1f3c0049d7c00729c8a5e09ba1dc497d037c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Wed, 7 Apr 2021 13:38:24 +0900
Subject: [PATCH] lsp: Gracefully fail if binary doesn't exist.

---
 helix-lsp/src/client.rs | 22 ++++++++++++++++------
 helix-lsp/src/lib.rs    | 10 ++++++----
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index 49495b84..64070be0 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -34,15 +34,25 @@ pub struct Client {
 }
 
 impl Client {
-    pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> (Self, Receiver<Call>) {
-        let mut process = Command::new(cmd)
+    pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> Result<(Self, Receiver<Call>)> {
+        // smol makes sure the process is reaped on drop, but using kill_on_drop(true) maybe?
+        let process = Command::new(cmd)
             .args(args)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
-            .spawn()
-            .expect("Failed to start language server");
-        // smol makes sure the process is reaped on drop, but using kill_on_drop(true) maybe?
+            .spawn();
+
+        // use std::io::ErrorKind;
+        let mut process = match process {
+            Ok(process) => process,
+            Err(err) => match err.kind() {
+                // ErrorKind::NotFound | ErrorKind::PermissionDenied => {
+                //     return Err(Error::Other(err.into()))
+                // }
+                _kind => return Err(Error::Other(err.into())),
+            },
+        };
 
         // TODO: do we need bufreader/writer here? or do we use async wrappers on unblock?
         let writer = BufWriter::new(process.stdin.take().expect("Failed to open stdin"));
@@ -65,7 +75,7 @@ impl Client {
         // TODO: async client.initialize()
         // maybe use an arc<atomic> flag
 
-        (client, incoming)
+        Ok((client, incoming))
     }
 
     fn next_request_id(&self) -> jsonrpc::Id {
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 89054345..40d2e477 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -104,7 +104,7 @@ use crate::select_all::SelectAll;
 use smol::channel::Receiver;
 
 pub struct Registry {
-    inner: HashMap<LanguageId, Arc<Client>>,
+    inner: HashMap<LanguageId, Option<Arc<Client>>>,
 
     pub incoming: SelectAll<Receiver<Call>>,
 }
@@ -140,17 +140,19 @@ impl Registry {
                     // TODO: lookup defaults for id (name, args)
 
                     // initialize a new client
-                    let (mut client, incoming) = Client::start(&ex, &config.command, &config.args);
+                    let (mut client, incoming) =
+                        Client::start(&ex, &config.command, &config.args).ok()?;
+
                     // TODO: run this async without blocking
                     smol::block_on(client.initialize()).unwrap();
 
                     s_incoming.push(incoming);
 
-                    Arc::new(client)
+                    Some(Arc::new(client))
                 })
                 .clone();
 
-            return Some(language_server);
+            return language_server;
         }
 
         None