WIP

Antonio Scandurra created

Change summary

crates/lsp/build.rs   | 36 ++++++++++++++++++++
crates/lsp/src/lib.rs | 81 +++++++++++++++++++++++++++++++-------------
2 files changed, 93 insertions(+), 24 deletions(-)

Detailed changes

crates/lsp/build.rs 🔗

@@ -0,0 +1,36 @@
+use std::{
+    env,
+    fs::{self, Permissions},
+    os::unix::prelude::PermissionsExt,
+    process::Command,
+};
+
+fn main() {
+    let target = env::var("TARGET").unwrap();
+    let rust_analyzer_filename = format!("rust-analyzer-{}", target);
+    let rust_analyzer_url = format!(
+        "https://github.com/rust-analyzer/rust-analyzer/releases/download/2021-10-18/{}.gz",
+        rust_analyzer_filename
+    );
+    println!(
+        "cargo:rustc-env=RUST_ANALYZER_FILENAME={}",
+        rust_analyzer_filename
+    );
+
+    let target_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+    let rust_analyzer_target_path = format!("{}/{}", target_dir, rust_analyzer_filename);
+    assert!(
+        Command::new("/bin/sh")
+            .arg("-c")
+            .arg(format!(
+                "curl -L {} | gunzip > {}",
+                rust_analyzer_url, rust_analyzer_target_path
+            ))
+            .status()
+            .unwrap()
+            .success(),
+        "failed to download rust-analyzer"
+    );
+    fs::set_permissions(rust_analyzer_target_path, Permissions::from_mode(0x755))
+        .expect("failed to make rust-analyzer executable");
+}

crates/lsp/src/lib.rs 🔗

@@ -42,24 +42,32 @@ struct Request<T> {
 }
 
 #[derive(Deserialize)]
-struct Error {
-    message: String,
+struct Response<'a> {
+    id: usize,
+    #[serde(default)]
+    error: Option<Error>,
+    #[serde(default, borrow)]
+    result: Option<&'a RawValue>,
+}
+
+#[derive(Serialize)]
+struct OutboundNotification<T> {
+    jsonrpc: &'static str,
+    method: &'static str,
+    params: T,
 }
 
 #[derive(Deserialize)]
-struct Notification<'a> {
-    method: String,
+struct InboundNotification<'a> {
+    #[serde(borrow)]
+    method: &'a str,
     #[serde(borrow)]
     params: &'a RawValue,
 }
 
 #[derive(Deserialize)]
-struct Response<'a> {
-    id: usize,
-    #[serde(default)]
-    error: Option<Error>,
-    #[serde(default, borrow)]
-    result: Option<&'a RawValue>,
+struct Error {
+    message: String,
 }
 
 impl LanguageServer {
@@ -91,7 +99,7 @@ impl LanguageServer {
 
                         buffer.resize(message_len, 0);
                         stdout.read_exact(&mut buffer).await?;
-                        if let Ok(Notification { .. }) = serde_json::from_slice(&buffer) {
+                        if let Ok(InboundNotification { .. }) = serde_json::from_slice(&buffer) {
                         } else if let Ok(Response { id, error, result }) =
                             serde_json::from_slice(&buffer)
                         {
@@ -146,19 +154,19 @@ impl LanguageServer {
     }
 
     async fn init(self: Arc<Self>) -> Result<()> {
-        let init_response = self
-            .request::<lsp_types::request::Initialize>(lsp_types::InitializeParams {
-                process_id: Default::default(),
-                root_path: Default::default(),
-                root_uri: Default::default(),
-                initialization_options: Default::default(),
-                capabilities: Default::default(),
-                trace: Default::default(),
-                workspace_folders: Default::default(),
-                client_info: Default::default(),
-                locale: Default::default(),
-            })
-            .await?;
+        self.request::<lsp_types::request::Initialize>(lsp_types::InitializeParams {
+            process_id: Default::default(),
+            root_path: Default::default(),
+            root_uri: Default::default(),
+            initialization_options: Default::default(),
+            capabilities: Default::default(),
+            trace: Default::default(),
+            workspace_folders: Default::default(),
+            client_info: Default::default(),
+            locale: Default::default(),
+        })
+        .await?;
+        self.notify::<lsp_types::notification::Initialized>(lsp_types::InitializedParams {})?;
         Ok(())
     }
 
@@ -198,4 +206,29 @@ impl LanguageServer {
             rx.recv().await?
         }
     }
+
+    pub fn notify<T: lsp_types::notification::Notification>(
+        &self,
+        params: T::Params,
+    ) -> Result<()> {
+        let message = serde_json::to_vec(&OutboundNotification {
+            jsonrpc: JSON_RPC_VERSION,
+            method: T::METHOD,
+            params,
+        })
+        .unwrap();
+        smol::block_on(self.outbound_tx.send(message))?;
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use gpui::TestAppContext;
+
+    #[gpui::test]
+    async fn test_basic(cx: TestAppContext) {
+        let server = LanguageServer::new();
+    }
 }