@@ -1,27 +1,26 @@
1+mod client;
12mod transport;
23
3−use transport::{Payload, Transport};
4−
5−use helix_core::{State, Transaction};
6−
7−// use std::collections::HashMap;
8−
94use jsonrpc_core as jsonrpc;
105use lsp_types as lsp;
11−use serde_json::Value;
12−
13−use serde::{Deserialize, Serialize};
146
15−pub use lsp::Position;
16−pub use lsp::Url;
7+pub use client::Client;
8+pub use lsp::{Position, Url};
179
18−use smol::{
19− channel::{Receiver, Sender},
20− io::{BufReader, BufWriter},
21− // prelude::*,
22− process::{Child, ChildStderr, Command, Stdio},
23− Executor,
24−};
10+use serde::{Deserialize, Serialize};
11+use thiserror::Error;
12+
13+#[derive(Error, Debug)]
14+pub enum Error {
15+ #[error("protocol error: {0}")]
16+ Rpc(#[from] jsonrpc::Error),
17+ #[error("failed to parse: {0}")]
18+ Parse(#[from] serde_json::Error),
19+ #[error("request timed out")]
20+ Timeout,
21+ #[error(transparent)]
22+ Other(#[from] anyhow::Error),
23+}
2524
2625pub mod util {
2726 use super::*;
@@ -68,209 +67,3 @@ impl Notification {
6867 }
6968 }
7069}
71−
72−pub struct Client {
73− _process: Child,
74− stderr: BufReader<ChildStderr>,
75−
76− outgoing: Sender<Payload>,
77− pub incoming: Receiver<Notification>,
78−
79− pub request_counter: u64,
80−
81− capabilities: Option<lsp::ServerCapabilities>,
82− // TODO: handle PublishDiagnostics Version
83− // diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
84−}
85−
86−impl Client {
87− pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> Self {
88− let mut process = Command::new(cmd)
89− .args(args)
90− .stdin(Stdio::piped())
91− .stdout(Stdio::piped())
92− .stderr(Stdio::piped())
93− .spawn()
94− .expect("Failed to start language server");
95− // smol makes sure the process is reaped on drop, but using kill_on_drop(true) maybe?
96−
97− // TODO: do we need bufreader/writer here? or do we use async wrappers on unblock?
98− let writer = BufWriter::new(process.stdin.take().expect("Failed to open stdin"));
99− let reader = BufReader::new(process.stdout.take().expect("Failed to open stdout"));
100− let stderr = BufReader::new(process.stderr.take().expect("Failed to open stderr"));
101−
102− let (incoming, outgoing) = Transport::start(ex, reader, writer);
103−
104− Client {
105− _process: process,
106− stderr,
107−
108− outgoing,
109− incoming,
110−
111− request_counter: 0,
112−
113− capabilities: None,
114− // diagnostics: HashMap::new(),
115− }
116− }
117−
118− fn next_request_id(&mut self) -> jsonrpc::Id {
119− let id = jsonrpc::Id::Num(self.request_counter);
120− self.request_counter += 1;
121− id
122− }
123−
124− fn to_params(value: Value) -> anyhow::Result<jsonrpc::Params> {
125− use jsonrpc::Params;
126−
127− let params = match value {
128− Value::Null => Params::None,
129− Value::Bool(_) | Value::Number(_) | Value::String(_) => Params::Array(vec![value]),
130− Value::Array(vec) => Params::Array(vec),
131− Value::Object(map) => Params::Map(map),
132− };
133−
134− Ok(params)
135− }
136−
137− pub async fn request<R: lsp::request::Request>(
138− &mut self,
139− params: R::Params,
140− ) -> anyhow::Result<R::Result>
141− where
142− R::Params: serde::Serialize,
143− R::Result: core::fmt::Debug, // TODO: temporary
144− {
145− let params = serde_json::to_value(params)?;
146−
147− let request = jsonrpc::MethodCall {
148− jsonrpc: Some(jsonrpc::Version::V2),
149− id: self.next_request_id(),
150− method: R::METHOD.to_string(),
151− params: Self::to_params(params)?,
152− };
153−
154− let (tx, rx) = smol::channel::bounded::<anyhow::Result<Value>>(1);
155−
156− self.outgoing
157− .send(Payload::Request {
158− chan: tx,
159− value: request,
160− })
161− .await?;
162−
163− let response = rx.recv().await??;
164−
165− let response = serde_json::from_value(response)?;
166−
167− // TODO: we should pass request to a sender thread via a channel
168− // so it can't be interleaved
169−
170− // TODO: responses can be out of order, we need to register a single shot response channel
171−
172− Ok(response)
173− }
174−
175− pub async fn notify<R: lsp::notification::Notification>(
176− &mut self,
177− params: R::Params,
178− ) -> anyhow::Result<()>
179− where
180− R::Params: serde::Serialize,
181− {
182− let params = serde_json::to_value(params)?;
183−
184− let notification = jsonrpc::Notification {
185− jsonrpc: Some(jsonrpc::Version::V2),
186− method: R::METHOD.to_string(),
187− params: Self::to_params(params)?,
188− };
189−
190− self.outgoing
191− .send(Payload::Notification(notification))
192− .await?;
193−
194− Ok(())
195− }
196−
197− // -------------------------------------------------------------------------------------------
198− // General messages
199− // -------------------------------------------------------------------------------------------
200−
201− pub async fn initialize(&mut self) -> anyhow::Result<()> {
202− // TODO: delay any requests that are triggered prior to initialize
203−
204− #[allow(deprecated)]
205− let params = lsp::InitializeParams {
206− process_id: Some(u64::from(std::process::id())),
207− root_path: None,
208− // root_uri: Some(lsp_types::Url::parse("file://localhost/")?),
209− root_uri: None, // set to project root in the future
210− initialization_options: None,
211− capabilities: lsp::ClientCapabilities::default(),
212− trace: None,
213− workspace_folders: None,
214− client_info: None,
215− };
216−
217− let response = self.request::<lsp::request::Initialize>(params).await?;
218− self.capabilities = Some(response.capabilities);
219−
220− // next up, notify<initialized>
221− self.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
222− .await?;
223−
224− Ok(())
225− }
226−
227− pub async fn shutdown(&mut self) -> anyhow::Result<()> {
228− self.request::<lsp::request::Shutdown>(()).await
229− }
230−
231− pub async fn exit(&mut self) -> anyhow::Result<()> {
232− self.notify::<lsp::notification::Exit>(()).await
233− }
234−
235− // -------------------------------------------------------------------------------------------
236− // Text document
237− // -------------------------------------------------------------------------------------------
238−
239− pub async fn text_document_did_open(&mut self, state: &State) -> anyhow::Result<()> {
240− self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
241− text_document: lsp::TextDocumentItem {
242− uri: lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
243− language_id: "rust".to_string(), // TODO: hardcoded for now
244− version: state.version,
245− text: String::from(&state.doc),
246− },
247− })
248− .await
249− }
250−
251− // TODO: trigger any time history.commit_revision happens
252− pub async fn text_document_did_change(
253− &mut self,
254− state: &State,
255− transaction: &Transaction,
256− ) -> anyhow::Result<()> {
257− self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
258− text_document: lsp::VersionedTextDocumentIdentifier::new(
259− lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
260− state.version,
261− ),
262− content_changes: vec![], // TODO: probably need old_state here too?
263− })
264− .await
265− }
266−
267− pub async fn text_document_did_close(&mut self) -> anyhow::Result<()> {
268− unimplemented!()
269− }
270−
271− // will_save / will_save_wait_until
272−
273− pub async fn text_document_did_save(&mut self) -> anyhow::Result<()> {
274− unimplemented!()
275− }
276−}