on master help sign in
Text change generation, RPC call handling.
cc6bdf8Blaž Hrastnik
helix-core/src/lib.rs+1−1
@@ -27,4 +27,4 @@ pub use diagnostic::Diagnostic;
27pub use history::History;
27pub use history::History;
28pub use state::State;
28pub use state::State;
29
29
30−pub use transaction::{Assoc, Change, ChangeSet, Transaction};
30+pub use transaction::{Assoc, Change, ChangeSet, Operation, Transaction};
helix-core/src/transaction.rs+8−1
@@ -5,8 +5,9 @@ use std::convert::TryFrom;
5/// (from, to, replacement)
5/// (from, to, replacement)
6pub type Change = (usize, usize, Option<Tendril>);
6pub type Change = (usize, usize, Option<Tendril>);
7
7
8+// TODO: pub(crate)
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[derive(Debug, Clone, PartialEq, Eq)]
9−pub(crate) enum Operation {
10+pub enum Operation {
10 /// Move cursor by n characters.
11 /// Move cursor by n characters.
11 Retain(usize),
12 Retain(usize),
12 /// Delete n characters.
13 /// Delete n characters.
@@ -40,6 +41,12 @@ impl ChangeSet {
40 }
41 }
41
42
42 // TODO: from iter
43 // TODO: from iter
44+ //
45+
46+ #[doc(hidden)] // used by lsp to convert to LSP changes
47+ pub fn changes(&self) -> &[Operation] {
48+ &self.changes
49+ }
43
50
44 #[must_use]
51 #[must_use]
45 fn len_after(&self) -> usize {
52 fn len_after(&self) -> usize {
helix-lsp/src/client.rs+127−10
@@ -1,11 +1,11 @@
1use crate::{
1use crate::{
2 transport::{Payload, Transport},
2 transport::{Payload, Transport},
3− Error, Notification,
3+ Call, Error,
4};
4};
5
5
6type Result<T> = core::result::Result<T, Error>;
6type Result<T> = core::result::Result<T, Error>;
7
7
8−use helix_core::{State, Transaction};
8+use helix_core::{ChangeSet, Transaction};
9use helix_view::Document;
9use helix_view::Document;
10
10
11// use std::collections::HashMap;
11// use std::collections::HashMap;
@@ -27,7 +27,7 @@ pub struct Client {
27 stderr: BufReader<ChildStderr>,
27 stderr: BufReader<ChildStderr>,
28
28
29 outgoing: Sender<Payload>,
29 outgoing: Sender<Payload>,
30− pub incoming: Receiver<Notification>,
30+ pub incoming: Receiver<Call>,
31
31
32 pub request_counter: u64,
32 pub request_counter: u64,
33
33
@@ -87,6 +87,7 @@ impl Client {
87 Ok(params)
87 Ok(params)
88 }
88 }
89
89
90+ /// Execute a RPC request on the language server.
90 pub async fn request<R: lsp::request::Request>(
91 pub async fn request<R: lsp::request::Request>(
91 &mut self,
92 &mut self,
92 params: R::Params,
93 params: R::Params,
@@ -126,6 +127,7 @@ impl Client {
126 Ok(response)
127 Ok(response)
127 }
128 }
128
129
130+ /// Send a RPC notification to the language server.
129 pub async fn notify<R: lsp::notification::Notification>(
131 pub async fn notify<R: lsp::notification::Notification>(
130 &mut self,
132 &mut self,
131 params: R::Params,
133 params: R::Params,
@@ -149,6 +151,35 @@ impl Client {
149 Ok(())
151 Ok(())
150 }
152 }
151
153
154+ /// Reply to a language server RPC call.
155+ pub async fn reply(
156+ &mut self,
157+ id: jsonrpc::Id,
158+ result: core::result::Result<Value, jsonrpc::Error>,
159+ ) -> Result<()> {
160+ use jsonrpc::{Failure, Output, Success, Version};
161+
162+ let output = match result {
163+ Ok(result) => Output::Success(Success {
164+ jsonrpc: Some(Version::V2),
165+ id,
166+ result,
167+ }),
168+ Err(error) => Output::Failure(Failure {
169+ jsonrpc: Some(Version::V2),
170+ id,
171+ error,
172+ }),
173+ };
174+
175+ self.outgoing
176+ .send(Payload::Response(output))
177+ .await
178+ .map_err(|e| Error::Other(e.into()))?;
179+
180+ Ok(())
181+ }
182+
152 // -------------------------------------------------------------------------------------------
183 // -------------------------------------------------------------------------------------------
153 // General messages
184 // General messages
154 // -------------------------------------------------------------------------------------------
185 // -------------------------------------------------------------------------------------------
@@ -163,7 +194,9 @@ impl Client {
163 // root_uri: Some(lsp_types::Url::parse("file://localhost/")?),
194 // root_uri: Some(lsp_types::Url::parse("file://localhost/")?),
164 root_uri: None, // set to project root in the future
195 root_uri: None, // set to project root in the future
165 initialization_options: None,
196 initialization_options: None,
166− capabilities: lsp::ClientCapabilities::default(),
197+ capabilities: lsp::ClientCapabilities {
198+ ..Default::default()
199+ },
167 trace: None,
200 trace: None,
168 workspace_folders: None,
201 workspace_folders: None,
169 client_info: None,
202 client_info: None,
@@ -203,23 +236,107 @@ impl Client {
203 .await
236 .await
204 }
237 }
205
238
239+ fn to_changes(changeset: &ChangeSet) -> Vec<lsp::TextDocumentContentChangeEvent> {
240+ let mut iter = changeset.changes().iter().peekable();
241+ let mut old_pos = 0;
242+
243+ let mut changes = Vec::new();
244+
245+ use crate::util::pos_to_lsp_pos;
246+ use helix_core::Operation::*;
247+
248+ // TEMP
249+ let rope = helix_core::Rope::from("");
250+ let old_text = rope.slice(..);
251+
252+ while let Some(change) = iter.next() {
253+ let len = match change {
254+ Delete(i) | Retain(i) => *i,
255+ Insert(_) => 0,
256+ };
257+ let old_end = old_pos + len;
258+
259+ match change {
260+ Retain(_) => {}
261+ Delete(_) => {
262+ let start = pos_to_lsp_pos(&old_text, old_pos);
263+ let end = pos_to_lsp_pos(&old_text, old_end);
264+
265+ // a subsequent ins means a replace, consume it
266+ if let Some(Insert(s)) = iter.peek() {
267+ iter.next();
268+
269+ // replacement
270+ changes.push(lsp::TextDocumentContentChangeEvent {
271+ range: Some(lsp::Range::new(start, end)),
272+ text: s.into(),
273+ range_length: None,
274+ });
275+ } else {
276+ // deletion
277+ changes.push(lsp::TextDocumentContentChangeEvent {
278+ range: Some(lsp::Range::new(start, end)),
279+ text: "".to_string(),
280+ range_length: None,
281+ });
282+ };
283+ }
284+ Insert(s) => {
285+ let start = pos_to_lsp_pos(&old_text, old_pos);
286+
287+ // insert
288+ changes.push(lsp::TextDocumentContentChangeEvent {
289+ range: Some(lsp::Range::new(start, start)),
290+ text: s.into(),
291+ range_length: None,
292+ });
293+ }
294+ }
295+ old_pos = old_end;
296+ }
297+
298+ changes
299+ }
300+
206 // TODO: trigger any time history.commit_revision happens
301 // TODO: trigger any time history.commit_revision happens
207 pub async fn text_document_did_change(
302 pub async fn text_document_did_change(
208 &mut self,
303 &mut self,
209 doc: &Document,
304 doc: &Document,
210 transaction: &Transaction,
305 transaction: &Transaction,
211 ) -> Result<()> {
306 ) -> Result<()> {
307+ // figure out what kind of sync the server supports
308+
309+ let capabilities = self.capabilities.as_ref().unwrap(); // TODO: needs post init
310+
311+ let sync_capabilities = match capabilities.text_document_sync {
312+ Some(lsp::TextDocumentSyncCapability::Kind(kind)) => kind,
313+ Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
314+ change: Some(kind),
315+ ..
316+ })) => kind,
317+ // None | SyncOptions { changes: None }
318+ _ => return Ok(()),
319+ };
320+
321+ let changes = match sync_capabilities {
322+ lsp::TextDocumentSyncKind::Full => {
323+ vec![lsp::TextDocumentContentChangeEvent {
324+ // range = None -> whole document
325+ range: None, //Some(Range)
326+ range_length: None, // u64 apparently deprecated
327+ text: "".to_string(),
328+ }] // TODO: probably need old_state here too?
329+ }
330+ lsp::TextDocumentSyncKind::Incremental => Self::to_changes(transaction.changes()),
331+ lsp::TextDocumentSyncKind::None => return Ok(()),
332+ };
333+
212 self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
334 self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
213 text_document: lsp::VersionedTextDocumentIdentifier::new(
335 text_document: lsp::VersionedTextDocumentIdentifier::new(
214 lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
336 lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
215 doc.version,
337 doc.version,
216 ),
338 ),
217− content_changes: vec![lsp::TextDocumentContentChangeEvent {
339+ content_changes: changes,
218− // range = None -> whole document
219− range: None, //Some(Range)
220− range_length: None, // u64 apparently deprecated
221− text: "".to_string(),
222− }], // TODO: probably need old_state here too?
223 })
340 })
224 .await
341 .await
225 }
342 }
helix-lsp/src/lib.rs+10−15
@@ -1,13 +1,12 @@
1mod client;
1mod client;
2mod transport;
2mod transport;
3
3
4−use jsonrpc_core as jsonrpc;
4+pub use jsonrpc_core as jsonrpc;
5−use lsp_types as lsp;
5+pub use lsp_types as lsp;
6
6
7pub use client::Client;
7pub use client::Client;
8pub use lsp::{Position, Url};
8pub use lsp::{Position, Url};
9
9
10−use serde::{Deserialize, Serialize};
11use thiserror::Error;
10use thiserror::Error;
12
11
13#[derive(Error, Debug)]
12#[derive(Error, Debug)]
@@ -30,19 +29,13 @@ pub mod util {
30 let line_start = doc.char_to_utf16_cu(line);
29 let line_start = doc.char_to_utf16_cu(line);
31 doc.utf16_cu_to_char(pos.character as usize + line_start)
30 doc.utf16_cu_to_char(pos.character as usize + line_start)
32 }
31 }
33−}
32+ pub fn pos_to_lsp_pos(doc: &helix_core::RopeSlice, pos: usize) -> lsp::Position {
33+ let line = doc.char_to_line(pos);
34+ let line_start = doc.char_to_utf16_cu(line);
35+ let col = doc.char_to_utf16_cu(pos) - line_start;
34
36
35−/// A type representing all possible values sent from the server to the client.
37+ lsp::Position::new(line as u64, col as u64)
36−#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
38+ }
37−#[serde(deny_unknown_fields)]
38−#[serde(untagged)]
39−enum Message {
40− /// A regular JSON-RPC request output (single response).
41− Output(jsonrpc::Output),
42− /// A notification.
43− Notification(jsonrpc::Notification),
44− /// A JSON-RPC request
45− Call(jsonrpc::Call),
46}
39}
47
40
48#[derive(Debug, PartialEq, Clone)]
41#[derive(Debug, PartialEq, Clone)]
@@ -67,3 +60,5 @@ impl Notification {
67 }
60 }
68 }
61 }
69}
62}
63+
64+pub use jsonrpc::Call;
helix-lsp/src/transport.rs+26−13
@@ -2,7 +2,7 @@ use std::collections::HashMap;
2
2
3use log::debug;
3use log::debug;
4
4
5−use crate::{Error, Message, Notification};
5+use crate::{Error, Notification};
6
6
7type Result<T> = core::result::Result<T, Error>;
7type Result<T> = core::result::Result<T, Error>;
8
8
@@ -24,10 +24,23 @@ pub(crate) enum Payload {
24 value: jsonrpc::MethodCall,
24 value: jsonrpc::MethodCall,
25 },
25 },
26 Notification(jsonrpc::Notification),
26 Notification(jsonrpc::Notification),
27+ Response(jsonrpc::Output),
28+}
29+
30+use serde::{Deserialize, Serialize};
31+/// A type representing all possible values sent from the server to the client.
32+#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
33+#[serde(deny_unknown_fields)]
34+#[serde(untagged)]
35+enum Message {
36+ /// A regular JSON-RPC request output (single response).
37+ Output(jsonrpc::Output),
38+ /// A JSON-RPC request or notification.
39+ Call(jsonrpc::Call),
27}
40}
28
41
29pub(crate) struct Transport {
42pub(crate) struct Transport {
30− incoming: Sender<Notification>, // TODO Notification | Call
43+ incoming: Sender<jsonrpc::Call>,
31 outgoing: Receiver<Payload>,
44 outgoing: Receiver<Payload>,
32
45
33 pending_requests: HashMap<jsonrpc::Id, Sender<Result<Value>>>,
46 pending_requests: HashMap<jsonrpc::Id, Sender<Result<Value>>>,
@@ -42,7 +55,7 @@ impl Transport {
42 ex: &Executor,
55 ex: &Executor,
43 reader: BufReader<ChildStdout>,
56 reader: BufReader<ChildStdout>,
44 writer: BufWriter<ChildStdin>,
57 writer: BufWriter<ChildStdin>,
45− ) -> (Receiver<Notification>, Sender<Payload>) {
58+ ) -> (Receiver<jsonrpc::Call>, Sender<Payload>) {
46 let (incoming, rx) = smol::channel::unbounded();
59 let (incoming, rx) = smol::channel::unbounded();
47 let (tx, outgoing) = smol::channel::unbounded();
60 let (tx, outgoing) = smol::channel::unbounded();
48
61
@@ -112,6 +125,10 @@ impl Transport {
112 let json = serde_json::to_string(&value)?;
125 let json = serde_json::to_string(&value)?;
113 self.send(json).await
126 self.send(json).await
114 }
127 }
128+ Payload::Response(error) => {
129+ let json = serde_json::to_string(&error)?;
130+ self.send(json).await
131+ }
115 }
132 }
116 }
133 }
117
134
@@ -131,24 +148,18 @@ impl Transport {
131 Ok(())
148 Ok(())
132 }
149 }
133
150
134− pub async fn recv_msg(&mut self, msg: Message) -> anyhow::Result<()> {
151+ async fn recv_msg(&mut self, msg: Message) -> anyhow::Result<()> {
135 match msg {
152 match msg {
136 Message::Output(output) => self.recv_response(output).await?,
153 Message::Output(output) => self.recv_response(output).await?,
137− Message::Notification(jsonrpc::Notification { method, params, .. }) => {
138− let notification = Notification::parse(&method, params);
139−
140− debug!("<- {} {:?}", method, notification);
141− self.incoming.send(notification).await?;
142− }
143 Message::Call(call) => {
154 Message::Call(call) => {
144− debug!("<- {:?}", call);
155+ self.incoming.send(call).await?;
145− // dispatch
156+ // let notification = Notification::parse(&method, params);
146 }
157 }
147 };
158 };
148 Ok(())
159 Ok(())
149 }
160 }
150
161
151− pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> {
162+ async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> {
152 match output {
163 match output {
153 jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => {
164 jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => {
154 debug!("<- {}", result);
165 debug!("<- {}", result);
@@ -191,6 +202,8 @@ impl Transport {
191 }
202 }
192 let msg = msg.unwrap();
203 let msg = msg.unwrap();
193
204
205+ debug!("<- {:?}", msg);
206+
194 self.recv_msg(msg).await.unwrap();
207 self.recv_msg(msg).await.unwrap();
195 }
208 }
196 }
209 }
helix-term/src/application.rs+50−37
@@ -433,8 +433,8 @@ impl<'a> Application<'a> {
433 event = reader.next().fuse() => {
433 event = reader.next().fuse() => {
434 self.handle_terminal_events(event).await
434 self.handle_terminal_events(event).await
435 }
435 }
436− notification = self.lsp.incoming.next().fuse() => {
436+ call = self.lsp.incoming.next().fuse() => {
437− self.handle_lsp_notification(notification).await
437+ self.handle_lsp_message(call).await
438 }
438 }
439 }
439 }
440 }
440 }
@@ -566,43 +566,56 @@ impl<'a> Application<'a> {
566 };
566 };
567 }
567 }
568
568
569− pub async fn handle_lsp_notification(&mut self, notification: Option<helix_lsp::Notification>) {
569+ pub async fn handle_lsp_message(&mut self, call: Option<helix_lsp::Call>) {
570− use helix_lsp::Notification;
570+ use helix_lsp::{Call, Notification};
571− match notification {
571+ match call {
572+ Some(Call::Notification(helix_lsp::jsonrpc::Notification {
573+ method, params, ..
574+ })) => {
575+ let notification = Notification::parse(&method, params);
576+ match notification {
572− Some(Notification::PublishDiagnostics(params)) => {
577+ Notification::PublishDiagnostics(params) => {
573− let path = Some(params.uri.to_file_path().unwrap());
578+ let path = Some(params.uri.to_file_path().unwrap());
574− let view = self
579+ let view = self
575− .editor
580+ .editor
576− .views
581+ .views
577− .iter_mut()
582+ .iter_mut()
578− .find(|view| view.doc.path == path);
583+ .find(|view| view.doc.path == path);
579−
584+
580− if let Some(view) = view {
585+ if let Some(view) = view {
581− let doc = view.doc.text().slice(..);
586+ let doc = view.doc.text().slice(..);
582− let diagnostics = params
587+ let diagnostics = params
583− .diagnostics
588+ .diagnostics
584− .into_iter()
589+ .into_iter()
585− .map(|diagnostic| {
590+ .map(|diagnostic| {
586− use helix_lsp::util::lsp_pos_to_pos;
591+ use helix_lsp::util::lsp_pos_to_pos;
587− let start = lsp_pos_to_pos(&doc, diagnostic.range.start);
592+ let start = lsp_pos_to_pos(&doc, diagnostic.range.start);
588− let end = lsp_pos_to_pos(&doc, diagnostic.range.end);
593+ let end = lsp_pos_to_pos(&doc, diagnostic.range.end);
589−
594+
590− helix_core::Diagnostic {
595+ helix_core::Diagnostic {
591− range: (start, end),
596+ range: (start, end),
592− line: diagnostic.range.start.line as usize,
597+ line: diagnostic.range.start.line as usize,
593− message: diagnostic.message,
598+ message: diagnostic.message,
594− // severity
599+ // severity
595− // code
600+ // code
596− // source
601+ // source
597− }
602+ }
598− })
603+ })
599− .collect();
604+ .collect();
600−
605+
601− view.doc.diagnostics = diagnostics;
606+ view.doc.diagnostics = diagnostics;
602−
607+
603− self.render();
608+ self.render();
609+ }
610+ }
611+ _ => unreachable!(),
604 }
612 }
605 }
613 }
614+ Some(Call::MethodCall(call)) => {
615+ // TODO: need to make Result<Value, Error>
616+
617+ unimplemented!("{:?}", call)
618+ }
606 _ => unreachable!(),
619 _ => unreachable!(),
607 }
620 }
608 }
621 }
helix-view/src/keymap.rs+3
@@ -82,6 +82,9 @@ use std::collections::HashMap;
82// = = align?
82// = = align?
83// + =
83// + =
84// }
84// }
85+//
86+// gd = goto definition
87+// gr = goto reference
85// }
88// }
86
89
87#[cfg(feature = "term")]
90#[cfg(feature = "term")]