on master help sign in
lsp: Work on syncing the state with the language server.
2ab069bBlaž Hrastnik
Cargo.lock+2−1
@@ -553,7 +553,6 @@ dependencies = [
553 "futures-util",
553 "futures-util",
554 "glob",
554 "glob",
555 "helix-core",
555 "helix-core",
556− "helix-view",
557 "jsonrpc-core",
556 "jsonrpc-core",
558 "log",
557 "log",
559 "lsp-types",
558 "lsp-types",
@@ -604,7 +603,9 @@ version = "0.1.0"
604dependencies = [
603dependencies = [
605 "anyhow",
604 "anyhow",
606 "crossterm",
605 "crossterm",
606+ "futures-util",
607 "helix-core",
607 "helix-core",
608+ "helix-lsp",
608 "once_cell",
609 "once_cell",
609 "smol",
610 "smol",
610 "tui",
611 "tui",
helix-lsp/Cargo.toml−1
@@ -8,7 +8,6 @@ edition = "2018"
8
8
9[dependencies]
9[dependencies]
10helix-core = { path = "../helix-core" }
10helix-core = { path = "../helix-core" }
11−helix-view = { path = "../helix-view" }
12
11
13once_cell = "1.4"
12once_cell = "1.4"
14
13
helix-lsp/src/client.rs+41−26
@@ -5,8 +5,7 @@ use crate::{
5
5
6type Result<T> = core::result::Result<T, Error>;
6type Result<T> = core::result::Result<T, Error>;
7
7
8−use helix_core::{ChangeSet, Transaction};
8+use helix_core::{ChangeSet, Rope, Transaction};
9−use helix_view::Document;
10
9
11// use std::collections::HashMap;
10// use std::collections::HashMap;
12use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::atomic::{AtomicU64, Ordering};
@@ -23,10 +22,6 @@ use smol::{
23 Executor,
22 Executor,
24};
23};
25
24
26−fn text_document_identifier(doc: &Document) -> lsp::TextDocumentIdentifier {
27− lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(doc.path().unwrap()).unwrap())
28−}
29−
30pub struct Client {
25pub struct Client {
31 _process: Child,
26 _process: Child,
32 stderr: BufReader<ChildStderr>,
27 stderr: BufReader<ChildStderr>,
@@ -232,13 +227,18 @@ impl Client {
232 // Text document
227 // Text document
233 // -------------------------------------------------------------------------------------------
228 // -------------------------------------------------------------------------------------------
234
229
235− pub async fn text_document_did_open(&self, doc: &Document) -> Result<()> {
230+ pub async fn text_document_did_open(
231+ &self,
232+ uri: lsp::Url,
233+ version: i32,
234+ doc: &Rope,
235+ ) -> Result<()> {
236 self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
236 self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
237 text_document: lsp::TextDocumentItem {
237 text_document: lsp::TextDocumentItem {
238− uri: lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
238+ uri,
239 language_id: "rust".to_string(), // TODO: hardcoded for now
239 language_id: "rust".to_string(), // TODO: hardcoded for now
240− version: doc.version,
240+ version,
241− text: String::from(doc.text()),
241+ text: String::from(doc),
242 },
242 },
243 })
243 })
244 .await
244 .await
@@ -290,6 +290,21 @@ impl Client {
290 };
290 };
291 }
291 }
292 Insert(s) => {
292 Insert(s) => {
293+ // TODO:
294+ // thread 'main' panicked at 'Attempt to index past end of slice: char index 1211, slice char length 0', /home/speed/.cargo/registry/src/github.com-1ecc6299db9ec823/ropey-1.2.0/src/slice.rs:301:9
295+ // stack backtrace:
296+ // 0: rust_begin_unwind
297+ // at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94/library/std/src/panicking.rs:493:5
298+ // 1: std::panicking::begin_panic_fmt
299+ // at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94/library/std/src/panicking.rs:435:5
300+ // 2: ropey::slice::RopeSlice::char_to_line
301+ // at /home/speed/.cargo/registry/src/github.com-1ecc6299db9ec823/ropey-1.2.0/src/slice.rs:301:9
302+ // 3: helix_lsp::util::pos_to_lsp_pos
303+ // at /home/speed/src/helix/helix-lsp/src/lib.rs:39:20
304+ // 4: helix_lsp::client::Client::to_changes
305+ // at /home/speed/src/helix/helix-lsp/src/client.rs:293:33
306+ // 5: helix_lsp::client::Client::text_document_did_change::{{closure}}
307+ // at /home/speed/src/helix/helix-lsp/src/client.rs:338:55
293 let start = pos_to_lsp_pos(&old_text, old_pos);
308 let start = pos_to_lsp_pos(&old_text, old_pos);
294
309
295 // insert
310 // insert
@@ -309,8 +324,8 @@ impl Client {
309 // TODO: trigger any time history.commit_revision happens
324 // TODO: trigger any time history.commit_revision happens
310 pub async fn text_document_did_change(
325 pub async fn text_document_did_change(
311 &self,
326 &self,
312− doc: &Document,
327+ text_document: lsp::VersionedTextDocumentIdentifier,
313− transaction: &Transaction,
328+ changes: &ChangeSet,
314 ) -> Result<()> {
329 ) -> Result<()> {
315 // figure out what kind of sync the server supports
330 // figure out what kind of sync the server supports
316
331
@@ -335,16 +350,12 @@ impl Client {
335 text: "".to_string(),
350 text: "".to_string(),
336 }] // TODO: probably need old_state here too?
351 }] // TODO: probably need old_state here too?
337 }
352 }
338− lsp::TextDocumentSyncKind::Incremental => Self::to_changes(transaction.changes()),
353+ lsp::TextDocumentSyncKind::Incremental => Self::to_changes(changes),
339 lsp::TextDocumentSyncKind::None => return Ok(()),
354 lsp::TextDocumentSyncKind::None => return Ok(()),
340 };
355 };
341
356
342 self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
357 self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
343− text_document: lsp::VersionedTextDocumentIdentifier::new(
358+ text_document,
344− // TODO: doc.into() Url
345− lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
346− doc.version,
347− ),
348 content_changes: changes,
359 content_changes: changes,
349 })
360 })
350 .await
361 .await
@@ -352,9 +363,12 @@ impl Client {
352
363
353 // TODO: impl into() TextDocumentIdentifier / VersionedTextDocumentIdentifier for Document.
364 // TODO: impl into() TextDocumentIdentifier / VersionedTextDocumentIdentifier for Document.
354
365
355− pub async fn text_document_did_close(&self, doc: &Document) -> Result<()> {
366+ pub async fn text_document_did_close(
367+ &self,
368+ text_document: lsp::TextDocumentIdentifier,
369+ ) -> Result<()> {
356 self.notify::<lsp::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams {
370 self.notify::<lsp::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams {
357− text_document: text_document_identifier(doc),
371+ text_document,
358 })
372 })
359 .await
373 .await
360 }
374 }
@@ -365,16 +379,17 @@ impl Client {
365 unimplemented!()
379 unimplemented!()
366 }
380 }
367
381
382+ pub async fn completion(
383+ &self,
384+ text_document: lsp::TextDocumentIdentifier,
385+ position: lsp::Position,
368− pub async fn completion(&self, doc: &Document) -> anyhow::Result<Vec<lsp::CompletionItem>> {
386+ ) -> anyhow::Result<Vec<lsp::CompletionItem>> {
369 // TODO: figure out what should happen when you complete with multiple cursors
387 // TODO: figure out what should happen when you complete with multiple cursors
370
388
371 let params = lsp::CompletionParams {
389 let params = lsp::CompletionParams {
372 text_document_position: lsp::TextDocumentPositionParams {
390 text_document_position: lsp::TextDocumentPositionParams {
373− text_document: text_document_identifier(doc),
391+ text_document,
374− position: crate::util::pos_to_lsp_pos(
392+ position,
375− &doc.text().slice(..),
376− doc.selection().cursor(),
377− ),
378 },
393 },
379 // TODO: support these tokens by async receiving and updating the choice list
394 // TODO: support these tokens by async receiving and updating the choice list
380 work_done_progress_params: lsp::WorkDoneProgressParams {
395 work_done_progress_params: lsp::WorkDoneProgressParams {
helix-term/src/application.rs+12−4
@@ -44,16 +44,23 @@ impl Application {
44 let mut editor = Editor::new();
44 let mut editor = Editor::new();
45 let size = terminal.size()?;
45 let size = terminal.size()?;
46
46
47+ let language_servers = helix_lsp::Registry::new();
48+ let language_server = language_servers.get("rust", &executor).unwrap();
49+
47 if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
50 if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
48 editor.open(file, (size.width, size.height))?;
51 editor.open(file, (size.width, size.height))?;
52+
53+ // TODO: do this everywhere
54+ editor
55+ .view_mut()
56+ .unwrap()
57+ .doc
58+ .set_language_server(Some(language_server.clone()));
49 }
59 }
50
60
51 let mut compositor = Compositor::new();
61 let mut compositor = Compositor::new();
52 compositor.push(Box::new(ui::EditorView::new()));
62 compositor.push(Box::new(ui::EditorView::new()));
53
63
54− let language_servers = helix_lsp::Registry::new();
55− let language_server = language_servers.get("rust", &executor).unwrap();
56−
57 let mut app = Self {
64 let mut app = Self {
58 editor,
65 editor,
59 terminal,
66 terminal,
@@ -90,8 +97,9 @@ impl Application {
90 pub async fn event_loop(&mut self) {
97 pub async fn event_loop(&mut self) {
91 let mut reader = EventStream::new();
98 let mut reader = EventStream::new();
92
99
100+ let doc = &self.editor.view().unwrap().doc;
93 self.language_server
101 self.language_server
94− .text_document_did_open(&self.editor.view().unwrap().doc)
102+ .text_document_did_open(doc.url().unwrap(), doc.version, doc.text())
95 .await
103 .await
96 .unwrap();
104 .unwrap();
97
105
helix-term/src/commands.rs+5−1
@@ -841,9 +841,13 @@ pub fn completion(cx: &mut Context) {
841 use std::time::Duration;
841 use std::time::Duration;
842
842
843 // TODO: blocking here is not ideal
843 // TODO: blocking here is not ideal
844+ let pos = helix_lsp::util::pos_to_lsp_pos(
845+ &cx.view.doc.text().slice(..),
846+ cx.view.doc.selection().cursor(),
847+ );
844 let res = smol::block_on(
848 let res = smol::block_on(
845 language_server
849 language_server
846− .completion(&cx.view.doc)
850+ .completion(cx.view.doc.identifier(), pos)
847 .timeout(Duration::from_secs(2)),
851 .timeout(Duration::from_secs(2)),
848 )
852 )
849 .expect("completion failed!")
853 .expect("completion failed!")
helix-term/src/ui/editor.rs+2−1
@@ -41,7 +41,8 @@ impl EditorView {
41 self.render_buffer(view, area, surface, theme);
41 self.render_buffer(view, area, surface, theme);
42
42
43 // clear with background color
43 // clear with background color
44+ // TODO: this seems to prevent setting style later
44− surface.set_style(viewport, theme.get("ui.background"));
45+ // surface.set_style(viewport, theme.get("ui.background"));
45
46
46 let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
47 let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
47 self.render_statusline(view, area, surface, theme);
48 self.render_statusline(view, area, surface, theme);
helix-view/Cargo.toml+2
@@ -13,6 +13,7 @@ default = ["term"]
13[dependencies]
13[dependencies]
14anyhow = "1"
14anyhow = "1"
15helix-core = { path = "../helix-core" }
15helix-core = { path = "../helix-core" }
16+helix-lsp = { path = "../helix-lsp"}
16
17
17# Conversion traits
18# Conversion traits
18# tui = { version = "0.12", default-features = false, features = ["crossterm"], optional = true}
19# tui = { version = "0.12", default-features = false, features = ["crossterm"], optional = true}
@@ -22,3 +23,4 @@ once_cell = "1.4"
22url = "2"
23url = "2"
23
24
24smol = "1"
25smol = "1"
26+futures-util = "0.3"
helix-view/src/document.rs+28−1
@@ -1,6 +1,7 @@
1use anyhow::Error;
1use anyhow::Error;
2use std::future::Future;
2use std::future::Future;
3use std::path::{Path, PathBuf};
3use std::path::{Path, PathBuf};
4+use std::sync::Arc;
4
5
5use helix_core::{
6use helix_core::{
6 syntax::LOADER, ChangeSet, Diagnostic, History, Position, Range, Rope, RopeSlice, Selection,
7 syntax::LOADER, ChangeSet, Diagnostic, History, Position, Range, Rope, RopeSlice, Selection,
@@ -35,6 +36,7 @@ pub struct Document {
35 pub version: i32, // should be usize?
36 pub version: i32, // should be usize?
36
37
37 pub diagnostics: Vec<Diagnostic>,
38 pub diagnostics: Vec<Diagnostic>,
39+ pub language_server: Option<Arc<helix_lsp::Client>>,
38}
40}
39
41
40/// Like std::mem::replace() except it allows the replacement value to be mapped from the
42/// Like std::mem::replace() except it allows the replacement value to be mapped from the
@@ -53,6 +55,8 @@ where
53 }
55 }
54}
56}
55
57
58+use futures_util::TryFutureExt;
59+use helix_lsp::lsp;
56use url::Url;
60use url::Url;
57
61
58impl Document {
62impl Document {
@@ -72,6 +76,7 @@ impl Document {
72 diagnostics: Vec::new(),
76 diagnostics: Vec::new(),
73 version: 0,
77 version: 0,
74 history: History::default(),
78 history: History::default(),
79+ language_server: None,
75 }
80 }
76 }
81 }
77
82
@@ -134,7 +139,7 @@ impl Document {
134 // TODO: flush?
139 // TODO: flush?
135
140
136 Ok(())
141 Ok(())
137− } // and_then(// lsp.send_text_saved_notification())
142+ } // and_then notify save
138 }
143 }
139
144
140 pub fn set_language(&mut self, scope: &str, scopes: &[String]) {
145 pub fn set_language(&mut self, scope: &str, scopes: &[String]) {
@@ -148,6 +153,10 @@ impl Document {
148 };
153 };
149 }
154 }
150
155
156+ pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
157+ self.language_server = language_server;
158+ }
159+
151 pub fn set_selection(&mut self, selection: Selection) {
160 pub fn set_selection(&mut self, selection: Selection) {
152 // TODO: use a transaction?
161 // TODO: use a transaction?
153 self.state.selection = selection;
162 self.state.selection = selection;
@@ -181,6 +190,14 @@ impl Document {
181 }
190 }
182
191
183 // TODO: map state.diagnostics over changes::map_pos too
192 // TODO: map state.diagnostics over changes::map_pos too
193+
194+ // emit lsp notification
195+ if let Some(language_server) = &self.language_server {
196+ let notify = language_server
197+ .text_document_did_change(self.versioned_identifier(), transaction.changes());
198+
199+ smol::block_on(notify).expect("failed to emit textDocument/didChange");
200+ }
184 }
201 }
185 success
202 success
186 }
203 }
@@ -263,4 +280,14 @@ impl Document {
263 // }
280 // }
264
281
265 // TODO: transact(Fn) ?
282 // TODO: transact(Fn) ?
283+
284+ // -- LSP methods
285+
286+ pub fn identifier(&self) -> lsp::TextDocumentIdentifier {
287+ lsp::TextDocumentIdentifier::new(self.url().unwrap())
288+ }
289+
290+ pub fn versioned_identifier(&self) -> lsp::VersionedTextDocumentIdentifier {
291+ lsp::VersionedTextDocumentIdentifier::new(self.url().unwrap(), self.version)
292+ }
266}
293}