From 2ab069bb3fc815394cccca50378b62932f3146f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 25 Dec 2020 17:20:09 +0900 Subject: [PATCH] lsp: Work on syncing the state with the language server. --- Cargo.lock | 3 +- helix-lsp/Cargo.toml | 1 - helix-lsp/src/client.rs | 67 +++++++++++++++++++++-------------- helix-term/src/application.rs | 16 ++++++--- helix-term/src/commands.rs | 6 +++- helix-term/src/ui/editor.rs | 3 +- helix-view/Cargo.toml | 2 ++ helix-view/src/document.rs | 29 ++++++++++++++- 8 files changed, 92 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33b7002ab..820a77ad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,7 +553,6 @@ dependencies = [ "futures-util", "glob", "helix-core", - "helix-view", "jsonrpc-core", "log", "lsp-types", @@ -604,7 +603,9 @@ version = "0.1.0" dependencies = [ "anyhow", "crossterm", + "futures-util", "helix-core", + "helix-lsp", "once_cell", "smol", "tui", diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index d22d8636d..70cf9c648 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" [dependencies] helix-core = { path = "../helix-core" } -helix-view = { path = "../helix-view" } once_cell = "1.4" diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index c3bcddd2b..7349792ad 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -5,8 +5,7 @@ use crate::{ type Result = core::result::Result; -use helix_core::{ChangeSet, Transaction}; -use helix_view::Document; +use helix_core::{ChangeSet, Rope, Transaction}; // use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; @@ -23,10 +22,6 @@ use smol::{ Executor, }; -fn text_document_identifier(doc: &Document) -> lsp::TextDocumentIdentifier { - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(doc.path().unwrap()).unwrap()) -} - pub struct Client { _process: Child, stderr: BufReader, @@ -232,13 +227,18 @@ impl Client { // Text document // ------------------------------------------------------------------------------------------- - pub async fn text_document_did_open(&self, doc: &Document) -> Result<()> { + pub async fn text_document_did_open( + &self, + uri: lsp::Url, + version: i32, + doc: &Rope, + ) -> Result<()> { self.notify::(lsp::DidOpenTextDocumentParams { text_document: lsp::TextDocumentItem { - uri: lsp::Url::from_file_path(doc.path().unwrap()).unwrap(), + uri, language_id: "rust".to_string(), // TODO: hardcoded for now - version: doc.version, - text: String::from(doc.text()), + version, + text: String::from(doc), }, }) .await @@ -290,6 +290,21 @@ impl Client { }; } Insert(s) => { + // TODO: + // 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 + // stack backtrace: + // 0: rust_begin_unwind + // at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94/library/std/src/panicking.rs:493:5 + // 1: std::panicking::begin_panic_fmt + // at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94/library/std/src/panicking.rs:435:5 + // 2: ropey::slice::RopeSlice::char_to_line + // at /home/speed/.cargo/registry/src/github.com-1ecc6299db9ec823/ropey-1.2.0/src/slice.rs:301:9 + // 3: helix_lsp::util::pos_to_lsp_pos + // at /home/speed/src/helix/helix-lsp/src/lib.rs:39:20 + // 4: helix_lsp::client::Client::to_changes + // at /home/speed/src/helix/helix-lsp/src/client.rs:293:33 + // 5: helix_lsp::client::Client::text_document_did_change::{{closure}} + // at /home/speed/src/helix/helix-lsp/src/client.rs:338:55 let start = pos_to_lsp_pos(&old_text, old_pos); // insert @@ -309,8 +324,8 @@ impl Client { // TODO: trigger any time history.commit_revision happens pub async fn text_document_did_change( &self, - doc: &Document, - transaction: &Transaction, + text_document: lsp::VersionedTextDocumentIdentifier, + changes: &ChangeSet, ) -> Result<()> { // figure out what kind of sync the server supports @@ -335,16 +350,12 @@ impl Client { text: "".to_string(), }] // TODO: probably need old_state here too? } - lsp::TextDocumentSyncKind::Incremental => Self::to_changes(transaction.changes()), + lsp::TextDocumentSyncKind::Incremental => Self::to_changes(changes), lsp::TextDocumentSyncKind::None => return Ok(()), }; self.notify::(lsp::DidChangeTextDocumentParams { - text_document: lsp::VersionedTextDocumentIdentifier::new( - // TODO: doc.into() Url - lsp::Url::from_file_path(doc.path().unwrap()).unwrap(), - doc.version, - ), + text_document, content_changes: changes, }) .await @@ -352,9 +363,12 @@ impl Client { // TODO: impl into() TextDocumentIdentifier / VersionedTextDocumentIdentifier for Document. - pub async fn text_document_did_close(&self, doc: &Document) -> Result<()> { + pub async fn text_document_did_close( + &self, + text_document: lsp::TextDocumentIdentifier, + ) -> Result<()> { self.notify::(lsp::DidCloseTextDocumentParams { - text_document: text_document_identifier(doc), + text_document, }) .await } @@ -365,16 +379,17 @@ impl Client { unimplemented!() } - pub async fn completion(&self, doc: &Document) -> anyhow::Result> { + pub async fn completion( + &self, + text_document: lsp::TextDocumentIdentifier, + position: lsp::Position, + ) -> anyhow::Result> { // TODO: figure out what should happen when you complete with multiple cursors let params = lsp::CompletionParams { text_document_position: lsp::TextDocumentPositionParams { - text_document: text_document_identifier(doc), - position: crate::util::pos_to_lsp_pos( - &doc.text().slice(..), - doc.selection().cursor(), - ), + text_document, + position, }, // TODO: support these tokens by async receiving and updating the choice list work_done_progress_params: lsp::WorkDoneProgressParams { diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 138f55c2e..004c5c618 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -44,16 +44,23 @@ impl Application { let mut editor = Editor::new(); let size = terminal.size()?; + let language_servers = helix_lsp::Registry::new(); + let language_server = language_servers.get("rust", &executor).unwrap(); + if let Some(file) = args.values_of_t::("files").unwrap().pop() { editor.open(file, (size.width, size.height))?; + + // TODO: do this everywhere + editor + .view_mut() + .unwrap() + .doc + .set_language_server(Some(language_server.clone())); } let mut compositor = Compositor::new(); compositor.push(Box::new(ui::EditorView::new())); - let language_servers = helix_lsp::Registry::new(); - let language_server = language_servers.get("rust", &executor).unwrap(); - let mut app = Self { editor, terminal, @@ -90,8 +97,9 @@ impl Application { pub async fn event_loop(&mut self) { let mut reader = EventStream::new(); + let doc = &self.editor.view().unwrap().doc; self.language_server - .text_document_did_open(&self.editor.view().unwrap().doc) + .text_document_did_open(doc.url().unwrap(), doc.version, doc.text()) .await .unwrap(); diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 862cf3121..594908647 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -841,9 +841,13 @@ pub fn completion(cx: &mut Context) { use std::time::Duration; // TODO: blocking here is not ideal + let pos = helix_lsp::util::pos_to_lsp_pos( + &cx.view.doc.text().slice(..), + cx.view.doc.selection().cursor(), + ); let res = smol::block_on( language_server - .completion(&cx.view.doc) + .completion(cx.view.doc.identifier(), pos) .timeout(Duration::from_secs(2)), ) .expect("completion failed!") diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 252219229..629cb85cb 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -41,7 +41,8 @@ impl EditorView { self.render_buffer(view, area, surface, theme); // clear with background color - surface.set_style(viewport, theme.get("ui.background")); + // TODO: this seems to prevent setting style later + // surface.set_style(viewport, theme.get("ui.background")); let area = Rect::new(0, viewport.height - 2, viewport.width, 1); self.render_statusline(view, area, surface, theme); diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 0a48b7217..658943c57 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -13,6 +13,7 @@ default = ["term"] [dependencies] anyhow = "1" helix-core = { path = "../helix-core" } +helix-lsp = { path = "../helix-lsp"} # Conversion traits # tui = { version = "0.12", default-features = false, features = ["crossterm"], optional = true} @@ -22,3 +23,4 @@ once_cell = "1.4" url = "2" smol = "1" +futures-util = "0.3" diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index d6246c341..785b0ca8e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1,6 +1,7 @@ use anyhow::Error; use std::future::Future; use std::path::{Path, PathBuf}; +use std::sync::Arc; use helix_core::{ syntax::LOADER, ChangeSet, Diagnostic, History, Position, Range, Rope, RopeSlice, Selection, @@ -35,6 +36,7 @@ pub struct Document { pub version: i32, // should be usize? pub diagnostics: Vec, + pub language_server: Option>, } /// Like std::mem::replace() except it allows the replacement value to be mapped from the @@ -53,6 +55,8 @@ where } } +use futures_util::TryFutureExt; +use helix_lsp::lsp; use url::Url; impl Document { @@ -72,6 +76,7 @@ impl Document { diagnostics: Vec::new(), version: 0, history: History::default(), + language_server: None, } } @@ -134,7 +139,7 @@ impl Document { // TODO: flush? Ok(()) - } // and_then(// lsp.send_text_saved_notification()) + } // and_then notify save } pub fn set_language(&mut self, scope: &str, scopes: &[String]) { @@ -148,6 +153,10 @@ impl Document { }; } + pub fn set_language_server(&mut self, language_server: Option>) { + self.language_server = language_server; + } + pub fn set_selection(&mut self, selection: Selection) { // TODO: use a transaction? self.state.selection = selection; @@ -181,6 +190,14 @@ impl Document { } // TODO: map state.diagnostics over changes::map_pos too + + // emit lsp notification + if let Some(language_server) = &self.language_server { + let notify = language_server + .text_document_did_change(self.versioned_identifier(), transaction.changes()); + + smol::block_on(notify).expect("failed to emit textDocument/didChange"); + } } success } @@ -263,4 +280,14 @@ impl Document { // } // TODO: transact(Fn) ? + + // -- LSP methods + + pub fn identifier(&self) -> lsp::TextDocumentIdentifier { + lsp::TextDocumentIdentifier::new(self.url().unwrap()) + } + + pub fn versioned_identifier(&self) -> lsp::VersionedTextDocumentIdentifier { + lsp::VersionedTextDocumentIdentifier::new(self.url().unwrap(), self.version) + } }