From 13cb442850ac3cd4b884a62711f0eb8740270d74 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 19 Oct 2020 16:09:44 +0900 Subject: wip: Fetching diagnostics, parsing notifications. --- helix-lsp/src/lib.rs | 243 ++++++++++++--------------------------------- helix-lsp/src/transport.rs | 189 +++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+), 178 deletions(-) create mode 100644 helix-lsp/src/transport.rs (limited to 'helix-lsp/src') diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index b371f337..41b3fdb2 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -1,18 +1,22 @@ +mod transport; + +use transport::{Payload, Transport}; + use std::collections::HashMap; use jsonrpc_core as jsonrpc; use lsp_types as lsp; +use serde_json::Value; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use smol::channel::{Receiver, Sender}; -use smol::io::{BufReader, BufWriter}; use smol::prelude::*; -use smol::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, Stdio}; -use smol::Executor; - -use futures_util::{select, FutureExt}; +use smol::{ + channel::{Receiver, Sender}, + io::{BufReader, BufWriter}, + process::{Child, ChildStderr, Command, Stdio}, + Executor, +}; /// A type representing all possible values sent from the server to the client. #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] @@ -27,14 +31,40 @@ enum Message { Call(jsonrpc::Call), } +#[derive(Debug, PartialEq, Clone)] +enum Notification {} + +impl Notification { + pub fn parse(method: &str, params: jsonrpc::Params) { + use lsp::notification::Notification as _; + + match method { + lsp::notification::PublishDiagnostics::METHOD => { + let params: lsp::PublishDiagnosticsParams = params + .parse() + .expect("Failed to parse PublishDiagnostics params"); + + println!("{:?}", params); + + // TODO: need to loop over diagnostics and distinguish them by URI + } + _ => println!("unhandled notification: {}", method), + } + } +} + pub struct Client { process: Child, stderr: BufReader, + outgoing: Sender, + incoming: Receiver, pub request_counter: u64, capabilities: Option, + // TODO: handle PublishDiagnostics Version + diagnostics: HashMap>, } impl Client { @@ -58,11 +88,14 @@ impl Client { Client { process, stderr, + outgoing, + incoming, request_counter: 0, capabilities: None, + diagnostics: HashMap::new(), } } @@ -187,191 +220,45 @@ impl Client { // Text document // ------------------------------------------------------------------------------------------- - pub async fn text_document_did_open(&mut self) -> anyhow::Result<()> { + pub async fn text_document_did_open( + &mut self, + state: &helix_core::State, + ) -> anyhow::Result<()> { self.notify::(lsp::DidOpenTextDocumentParams { text_document: lsp::TextDocumentItem { - uri: lsp::Url::parse(".")?, - language_id: "rust".to_string(), + uri: lsp::Url::from_file_path( + std::fs::canonicalize(state.path.as_ref().unwrap()).unwrap(), + ) + .unwrap(), + language_id: "rust".to_string(), // TODO: hardcoded for now version: 0, - text: "".to_string(), + text: String::from(&state.doc), }, }) .await } - pub async fn text_document_did_change(&mut self) -> anyhow::Result<()> { - unimplemented!() - } - - // will_save / will_save_wait_until - - pub async fn text_document_did_save(&mut self) -> anyhow::Result<()> { - unimplemented!() + // TODO: trigger any time history.commit_revision happens + pub async fn text_document_did_change( + &mut self, + state: &helix_core::State, + ) -> anyhow::Result<()> { + self.notify::(lsp::DidSaveTextDocumentParams { + text_document: lsp::TextDocumentIdentifier::new( + lsp::Url::from_file_path(state.path.as_ref().unwrap()).unwrap(), + ), + text: None, // TODO? + }) + .await } pub async fn text_document_did_close(&mut self) -> anyhow::Result<()> { unimplemented!() } -} - -enum Payload { - Request { - chan: Sender>, - value: jsonrpc::MethodCall, - }, - Notification(jsonrpc::Notification), -} - -struct Transport { - incoming: Sender, - outgoing: Receiver, - - pending_requests: HashMap>>, - headers: HashMap, - - writer: BufWriter, - reader: BufReader, -} - -impl Transport { - pub fn start( - ex: &Executor, - reader: BufReader, - writer: BufWriter, - ) -> (Receiver, Sender) { - let (incoming, rx) = smol::channel::unbounded(); - let (tx, outgoing) = smol::channel::unbounded(); - - let transport = Self { - reader, - writer, - incoming, - outgoing, - pending_requests: Default::default(), - headers: Default::default(), - }; - - ex.spawn(transport.duplex()).detach(); - - (rx, tx) - } - - async fn recv( - reader: &mut (impl AsyncBufRead + Unpin), - headers: &mut HashMap, - ) -> Result { - // read headers - loop { - let mut header = String::new(); - // detect pipe closed if 0 - reader.read_line(&mut header).await?; - let header = header.trim(); - - if header.is_empty() { - break; - } - - let parts: Vec<&str> = header.split(": ").collect(); - if parts.len() != 2 { - // return Err(Error::new(ErrorKind::Other, "Failed to parse header")); - panic!() - } - headers.insert(parts[0].to_string(), parts[1].to_string()); - } - - // find content-length - let content_length = headers.get("Content-Length").unwrap().parse().unwrap(); - - let mut content = vec![0; content_length]; - reader.read_exact(&mut content).await?; - let msg = String::from_utf8(content).unwrap(); - - // read data - - // try parsing as output (server response) or call (server request) - let output: serde_json::Result = serde_json::from_str(&msg); - - Ok(output?) - } - pub async fn send_payload(&mut self, payload: Payload) -> anyhow::Result<()> { - match payload { - Payload::Request { chan, value } => { - self.pending_requests.insert(value.id.clone(), chan); - - let json = serde_json::to_string(&value)?; - self.send(json).await - } - Payload::Notification(value) => { - let json = serde_json::to_string(&value)?; - self.send(json).await - } - } - } - - pub async fn send(&mut self, request: String) -> anyhow::Result<()> { - println!("-> {}", request); - - // send the headers - self.writer - .write_all(format!("Content-Length: {}\r\n\r\n", request.len()).as_bytes()) - .await?; - - // send the body - self.writer.write_all(request.as_bytes()).await?; - - self.writer.flush().await?; - - Ok(()) - } - - pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> { - match output { - jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => { - println!("<- {}", result); - - let tx = self - .pending_requests - .remove(&id) - .expect("pending_request with id not found!"); - tx.send(Ok(result)).await?; - } - jsonrpc::Output::Failure(_) => panic!("recv fail"), - msg => unimplemented!("{:?}", msg), - } - Ok(()) - } + // will_save / will_save_wait_until - pub async fn duplex(mut self) { - loop { - select! { - // client -> server - msg = self.outgoing.next().fuse() => { - if msg.is_none() { - break; - } - let msg = msg.unwrap(); - - self.send_payload(msg).await.unwrap(); - } - // server <- client - msg = Self::recv(&mut self.reader, &mut self.headers).fuse() => { - if msg.is_err() { - break; - } - let msg = msg.unwrap(); - - match msg { - Message::Output(output) => self.recv_response(output).await.unwrap(), - Message::Notification(_) => { - // dispatch - } - Message::Call(_) => { - // dispatch - } - }; - } - } - } + pub async fn text_document_did_save(&mut self) -> anyhow::Result<()> { + unimplemented!() } } diff --git a/helix-lsp/src/transport.rs b/helix-lsp/src/transport.rs new file mode 100644 index 00000000..c44ffa91 --- /dev/null +++ b/helix-lsp/src/transport.rs @@ -0,0 +1,189 @@ +use std::collections::HashMap; + +use crate::{Message, Notification}; + +use jsonrpc_core as jsonrpc; +use lsp_types as lsp; +use serde_json::Value; + +use smol::prelude::*; + +use smol::{ + channel::{Receiver, Sender}, + io::{BufReader, BufWriter}, + process::{ChildStderr, ChildStdin, ChildStdout}, + Executor, +}; + +pub(crate) enum Payload { + Request { + chan: Sender>, + value: jsonrpc::MethodCall, + }, + Notification(jsonrpc::Notification), +} + +pub(crate) struct Transport { + incoming: Sender, + outgoing: Receiver, + + pending_requests: HashMap>>, + headers: HashMap, + + writer: BufWriter, + reader: BufReader, +} + +impl Transport { + pub fn start( + ex: &Executor, + reader: BufReader, + writer: BufWriter, + ) -> (Receiver, Sender) { + let (incoming, rx) = smol::channel::unbounded(); + let (tx, outgoing) = smol::channel::unbounded(); + + let transport = Self { + reader, + writer, + incoming, + outgoing, + pending_requests: Default::default(), + headers: Default::default(), + }; + + ex.spawn(transport.duplex()).detach(); + + (rx, tx) + } + + async fn recv( + reader: &mut (impl AsyncBufRead + Unpin), + headers: &mut HashMap, + ) -> Result { + // read headers + loop { + let mut header = String::new(); + // detect pipe closed if 0 + reader.read_line(&mut header).await?; + let header = header.trim(); + + if header.is_empty() { + break; + } + + let parts: Vec<&str> = header.split(": ").collect(); + if parts.len() != 2 { + // return Err(Error::new(ErrorKind::Other, "Failed to parse header")); + panic!() + } + headers.insert(parts[0].to_string(), parts[1].to_string()); + } + + // find content-length + let content_length = headers.get("Content-Length").unwrap().parse().unwrap(); + + let mut content = vec![0; content_length]; + reader.read_exact(&mut content).await?; + let msg = String::from_utf8(content).unwrap(); + + // read data + + // try parsing as output (server response) or call (server request) + let output: serde_json::Result = serde_json::from_str(&msg); + + Ok(output?) + } + + pub async fn send_payload(&mut self, payload: Payload) -> anyhow::Result<()> { + match payload { + Payload::Request { chan, value } => { + self.pending_requests.insert(value.id.clone(), chan); + + let json = serde_json::to_string(&value)?; + self.send(json).await + } + Payload::Notification(value) => { + let json = serde_json::to_string(&value)?; + self.send(json).await + } + } + } + + pub async fn send(&mut self, request: String) -> anyhow::Result<()> { + println!("-> {}", request); + + // send the headers + self.writer + .write_all(format!("Content-Length: {}\r\n\r\n", request.len()).as_bytes()) + .await?; + + // send the body + self.writer.write_all(request.as_bytes()).await?; + + self.writer.flush().await?; + + Ok(()) + } + + pub async fn recv_msg(&mut self, msg: Message) -> anyhow::Result<()> { + match msg { + Message::Output(output) => self.recv_response(output).await?, + Message::Notification(jsonrpc::Notification { method, params, .. }) => { + let notification = Notification::parse(&method, params); + + println!("<- {} {:?}", method, notification); + // dispatch + } + Message::Call(call) => { + println!("<- {:?}", call); + // dispatch + } + _ => unreachable!(), + }; + Ok(()) + } + + pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> { + match output { + jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => { + println!("<- {}", result); + + let tx = self + .pending_requests + .remove(&id) + .expect("pending_request with id not found!"); + tx.send(Ok(result)).await?; + } + jsonrpc::Output::Failure(_) => panic!("recv fail"), + msg => unimplemented!("{:?}", msg), + } + Ok(()) + } + + pub async fn duplex(mut self) { + use futures_util::{select, FutureExt}; + loop { + select! { + // client -> server + msg = self.outgoing.next().fuse() => { + if msg.is_none() { + break; + } + let msg = msg.unwrap(); + + self.send_payload(msg).await.unwrap(); + } + // server <- client + msg = Self::recv(&mut self.reader, &mut self.headers).fuse() => { + if msg.is_err() { + break; + } + let msg = msg.unwrap(); + + self.recv_msg(msg).await.unwrap(); + } + } + } + } +} -- cgit v1.2.3-70-g09d2