lsp: Work on syncing the state with the language server.
This commit is contained in:
parent
cd16df19c1
commit
2ab069bb3f
8 changed files with 92 additions and 35 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -553,7 +553,6 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"glob",
|
"glob",
|
||||||
"helix-core",
|
"helix-core",
|
||||||
"helix-view",
|
|
||||||
"jsonrpc-core",
|
"jsonrpc-core",
|
||||||
"log",
|
"log",
|
||||||
"lsp-types",
|
"lsp-types",
|
||||||
|
@ -604,7 +603,9 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"futures-util",
|
||||||
"helix-core",
|
"helix-core",
|
||||||
|
"helix-lsp",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"smol",
|
"smol",
|
||||||
"tui",
|
"tui",
|
||||||
|
|
|
@ -8,7 +8,6 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
helix-core = { path = "../helix-core" }
|
helix-core = { path = "../helix-core" }
|
||||||
helix-view = { path = "../helix-view" }
|
|
||||||
|
|
||||||
once_cell = "1.4"
|
once_cell = "1.4"
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ use crate::{
|
||||||
|
|
||||||
type Result<T> = core::result::Result<T, Error>;
|
type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
use helix_core::{ChangeSet, Transaction};
|
use helix_core::{ChangeSet, Rope, Transaction};
|
||||||
use helix_view::Document;
|
|
||||||
|
|
||||||
// use std::collections::HashMap;
|
// use std::collections::HashMap;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
@ -23,10 +22,6 @@ use smol::{
|
||||||
Executor,
|
Executor,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn text_document_identifier(doc: &Document) -> lsp::TextDocumentIdentifier {
|
|
||||||
lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(doc.path().unwrap()).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
_process: Child,
|
_process: Child,
|
||||||
stderr: BufReader<ChildStderr>,
|
stderr: BufReader<ChildStderr>,
|
||||||
|
@ -232,13 +227,18 @@ impl Client {
|
||||||
// Text document
|
// 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::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
|
self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
|
||||||
text_document: lsp::TextDocumentItem {
|
text_document: lsp::TextDocumentItem {
|
||||||
uri: lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
|
uri,
|
||||||
language_id: "rust".to_string(), // TODO: hardcoded for now
|
language_id: "rust".to_string(), // TODO: hardcoded for now
|
||||||
version: doc.version,
|
version,
|
||||||
text: String::from(doc.text()),
|
text: String::from(doc),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -290,6 +290,21 @@ impl Client {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Insert(s) => {
|
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);
|
let start = pos_to_lsp_pos(&old_text, old_pos);
|
||||||
|
|
||||||
// insert
|
// insert
|
||||||
|
@ -309,8 +324,8 @@ impl Client {
|
||||||
// TODO: trigger any time history.commit_revision happens
|
// TODO: trigger any time history.commit_revision happens
|
||||||
pub async fn text_document_did_change(
|
pub async fn text_document_did_change(
|
||||||
&self,
|
&self,
|
||||||
doc: &Document,
|
text_document: lsp::VersionedTextDocumentIdentifier,
|
||||||
transaction: &Transaction,
|
changes: &ChangeSet,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// figure out what kind of sync the server supports
|
// figure out what kind of sync the server supports
|
||||||
|
|
||||||
|
@ -335,16 +350,12 @@ impl Client {
|
||||||
text: "".to_string(),
|
text: "".to_string(),
|
||||||
}] // TODO: probably need old_state here too?
|
}] // 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(()),
|
lsp::TextDocumentSyncKind::None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
|
self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
|
||||||
text_document: lsp::VersionedTextDocumentIdentifier::new(
|
text_document,
|
||||||
// TODO: doc.into() Url
|
|
||||||
lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
|
|
||||||
doc.version,
|
|
||||||
),
|
|
||||||
content_changes: changes,
|
content_changes: changes,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -352,9 +363,12 @@ impl Client {
|
||||||
|
|
||||||
// TODO: impl into() TextDocumentIdentifier / VersionedTextDocumentIdentifier for Document.
|
// 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::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams {
|
self.notify::<lsp::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams {
|
||||||
text_document: text_document_identifier(doc),
|
text_document,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -365,16 +379,17 @@ impl Client {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn completion(&self, doc: &Document) -> anyhow::Result<Vec<lsp::CompletionItem>> {
|
pub async fn completion(
|
||||||
|
&self,
|
||||||
|
text_document: lsp::TextDocumentIdentifier,
|
||||||
|
position: lsp::Position,
|
||||||
|
) -> anyhow::Result<Vec<lsp::CompletionItem>> {
|
||||||
// TODO: figure out what should happen when you complete with multiple cursors
|
// TODO: figure out what should happen when you complete with multiple cursors
|
||||||
|
|
||||||
let params = lsp::CompletionParams {
|
let params = lsp::CompletionParams {
|
||||||
text_document_position: lsp::TextDocumentPositionParams {
|
text_document_position: lsp::TextDocumentPositionParams {
|
||||||
text_document: text_document_identifier(doc),
|
text_document,
|
||||||
position: crate::util::pos_to_lsp_pos(
|
position,
|
||||||
&doc.text().slice(..),
|
|
||||||
doc.selection().cursor(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
// TODO: support these tokens by async receiving and updating the choice list
|
// TODO: support these tokens by async receiving and updating the choice list
|
||||||
work_done_progress_params: lsp::WorkDoneProgressParams {
|
work_done_progress_params: lsp::WorkDoneProgressParams {
|
||||||
|
|
|
@ -44,16 +44,23 @@ impl Application {
|
||||||
let mut editor = Editor::new();
|
let mut editor = Editor::new();
|
||||||
let size = terminal.size()?;
|
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::<PathBuf>("files").unwrap().pop() {
|
if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
|
||||||
editor.open(file, (size.width, size.height))?;
|
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();
|
let mut compositor = Compositor::new();
|
||||||
compositor.push(Box::new(ui::EditorView::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 {
|
let mut app = Self {
|
||||||
editor,
|
editor,
|
||||||
terminal,
|
terminal,
|
||||||
|
@ -90,8 +97,9 @@ impl Application {
|
||||||
pub async fn event_loop(&mut self) {
|
pub async fn event_loop(&mut self) {
|
||||||
let mut reader = EventStream::new();
|
let mut reader = EventStream::new();
|
||||||
|
|
||||||
|
let doc = &self.editor.view().unwrap().doc;
|
||||||
self.language_server
|
self.language_server
|
||||||
.text_document_did_open(&self.editor.view().unwrap().doc)
|
.text_document_did_open(doc.url().unwrap(), doc.version, doc.text())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -841,9 +841,13 @@ pub fn completion(cx: &mut Context) {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// TODO: blocking here is not ideal
|
// 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(
|
let res = smol::block_on(
|
||||||
language_server
|
language_server
|
||||||
.completion(&cx.view.doc)
|
.completion(cx.view.doc.identifier(), pos)
|
||||||
.timeout(Duration::from_secs(2)),
|
.timeout(Duration::from_secs(2)),
|
||||||
)
|
)
|
||||||
.expect("completion failed!")
|
.expect("completion failed!")
|
||||||
|
|
|
@ -41,7 +41,8 @@ impl EditorView {
|
||||||
self.render_buffer(view, area, surface, theme);
|
self.render_buffer(view, area, surface, theme);
|
||||||
|
|
||||||
// clear with background color
|
// 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);
|
let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
|
||||||
self.render_statusline(view, area, surface, theme);
|
self.render_statusline(view, area, surface, theme);
|
||||||
|
|
|
@ -13,6 +13,7 @@ default = ["term"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
helix-core = { path = "../helix-core" }
|
helix-core = { path = "../helix-core" }
|
||||||
|
helix-lsp = { path = "../helix-lsp"}
|
||||||
|
|
||||||
# Conversion traits
|
# Conversion traits
|
||||||
# tui = { version = "0.12", default-features = false, features = ["crossterm"], optional = true}
|
# tui = { version = "0.12", default-features = false, features = ["crossterm"], optional = true}
|
||||||
|
@ -22,3 +23,4 @@ once_cell = "1.4"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
||||||
smol = "1"
|
smol = "1"
|
||||||
|
futures-util = "0.3"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
syntax::LOADER, ChangeSet, Diagnostic, History, Position, Range, Rope, RopeSlice, Selection,
|
syntax::LOADER, ChangeSet, Diagnostic, History, Position, Range, Rope, RopeSlice, Selection,
|
||||||
|
@ -35,6 +36,7 @@ pub struct Document {
|
||||||
pub version: i32, // should be usize?
|
pub version: i32, // should be usize?
|
||||||
|
|
||||||
pub diagnostics: Vec<Diagnostic>,
|
pub diagnostics: Vec<Diagnostic>,
|
||||||
|
pub language_server: Option<Arc<helix_lsp::Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like std::mem::replace() except it allows the replacement value to be mapped from the
|
/// 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;
|
use url::Url;
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
|
@ -72,6 +76,7 @@ impl Document {
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
version: 0,
|
version: 0,
|
||||||
history: History::default(),
|
history: History::default(),
|
||||||
|
language_server: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +139,7 @@ impl Document {
|
||||||
// TODO: flush?
|
// TODO: flush?
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} // and_then(// lsp.send_text_saved_notification())
|
} // and_then notify save
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_language(&mut self, scope: &str, scopes: &[String]) {
|
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<Arc<helix_lsp::Client>>) {
|
||||||
|
self.language_server = language_server;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_selection(&mut self, selection: Selection) {
|
pub fn set_selection(&mut self, selection: Selection) {
|
||||||
// TODO: use a transaction?
|
// TODO: use a transaction?
|
||||||
self.state.selection = selection;
|
self.state.selection = selection;
|
||||||
|
@ -181,6 +190,14 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: map state.diagnostics over changes::map_pos too
|
// 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
|
success
|
||||||
}
|
}
|
||||||
|
@ -263,4 +280,14 @@ impl Document {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO: transact(Fn) ?
|
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue