aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2020-10-20 04:58:34 +0000
committerBlaž Hrastnik2020-12-03 04:10:32 +0000
commitf9bfba4d96f80eb41beb91702558f6f165a0e70f (patch)
treee2e0296a01645cdcb7782a9bd8b4f1a55d3fbfd9
parent64b5b23315f12125a2c5b2f810fe5ac285bdfa79 (diff)
Reroute LSP notification events into the main app event loop.
-rw-r--r--Cargo.lock1
-rw-r--r--helix-core/src/diagnostic.rs1
-rw-r--r--helix-core/src/lib.rs2
-rw-r--r--helix-core/src/state.rs10
-rw-r--r--helix-lsp/src/lib.rs33
-rw-r--r--helix-lsp/src/transport.rs14
-rw-r--r--helix-term/Cargo.toml2
-rw-r--r--helix-term/src/application.rs281
-rw-r--r--helix-term/src/main.rs12
-rw-r--r--helix-view/src/keymap.rs4
-rw-r--r--helix-view/src/view.rs2
11 files changed, 207 insertions, 155 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c4beee76..2e329fd1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -472,6 +472,7 @@ dependencies = [
"anyhow",
"clap",
"crossterm",
+ "futures-util",
"helix-core",
"helix-lsp",
"helix-view",
diff --git a/helix-core/src/diagnostic.rs b/helix-core/src/diagnostic.rs
new file mode 100644
index 00000000..aee648aa
--- /dev/null
+++ b/helix-core/src/diagnostic.rs
@@ -0,0 +1 @@
+pub struct Diagnostic {}
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index 62d23a10..8458c36f 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -1,4 +1,5 @@
#![allow(unused)]
+mod diagnostic;
pub mod graphemes;
mod history;
pub mod indent;
@@ -22,6 +23,7 @@ pub use selection::Range;
pub use selection::Selection;
pub use syntax::Syntax;
+pub use diagnostic::Diagnostic;
pub use history::History;
pub use state::State;
diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs
index 35e20aef..0f94f696 100644
--- a/helix-core/src/state.rs
+++ b/helix-core/src/state.rs
@@ -1,6 +1,6 @@
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
use crate::syntax::LOADER;
-use crate::{ChangeSet, Position, Range, Rope, RopeSlice, Selection, Syntax};
+use crate::{ChangeSet, Diagnostic, Position, Range, Rope, RopeSlice, Selection, Syntax};
use anyhow::Error;
use std::path::PathBuf;
@@ -28,6 +28,8 @@ pub struct State {
/// Pending changes since last history commit.
pub changes: ChangeSet,
pub old_state: Option<(Rope, Selection)>,
+
+ pub diagnostics: Vec<Diagnostic>,
}
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -58,12 +60,13 @@ impl State {
syntax: None,
changes,
old_state,
+ diagnostics: Vec::new(),
}
}
// TODO: passing scopes here is awkward
pub fn load(path: PathBuf, scopes: &[String]) -> Result<Self, Error> {
- use std::{env, fs::File, io::BufReader, path::PathBuf};
+ use std::{env, fs::File, io::BufReader};
let _current_dir = env::current_dir()?;
let doc = Rope::from_reader(BufReader::new(File::open(path.clone())?))?;
@@ -81,7 +84,8 @@ impl State {
state.syntax = Some(syntax);
};
- state.path = Some(path);
+ // canonicalize path to absolute value
+ state.path = Some(std::fs::canonicalize(path)?);
Ok(state)
}
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 41b3fdb2..3598a594 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -2,7 +2,7 @@ mod transport;
use transport::{Payload, Transport};
-use std::collections::HashMap;
+// use std::collections::HashMap;
use jsonrpc_core as jsonrpc;
use lsp_types as lsp;
@@ -32,10 +32,12 @@ enum Message {
}
#[derive(Debug, PartialEq, Clone)]
-enum Notification {}
+pub enum Notification {
+ PublishDiagnostics(lsp::PublishDiagnosticsParams),
+}
impl Notification {
- pub fn parse(method: &str, params: jsonrpc::Params) {
+ pub fn parse(method: &str, params: jsonrpc::Params) -> Notification {
use lsp::notification::Notification as _;
match method {
@@ -44,11 +46,10 @@ impl Notification {
.parse()
.expect("Failed to parse PublishDiagnostics params");
- println!("{:?}", params);
-
// TODO: need to loop over diagnostics and distinguish them by URI
+ Notification::PublishDiagnostics(params)
}
- _ => println!("unhandled notification: {}", method),
+ _ => unimplemented!("unhandled notification: {}", method),
}
}
}
@@ -58,13 +59,13 @@ pub struct Client {
stderr: BufReader<ChildStderr>,
outgoing: Sender<Payload>,
- incoming: Receiver<Message>,
+ pub incoming: Receiver<Notification>,
pub request_counter: u64,
capabilities: Option<lsp::ServerCapabilities>,
// TODO: handle PublishDiagnostics Version
- diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
+ // diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
}
impl Client {
@@ -95,7 +96,7 @@ impl Client {
request_counter: 0,
capabilities: None,
- diagnostics: HashMap::new(),
+ // diagnostics: HashMap::new(),
}
}
@@ -226,10 +227,7 @@ impl Client {
) -> anyhow::Result<()> {
self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
text_document: lsp::TextDocumentItem {
- uri: lsp::Url::from_file_path(
- std::fs::canonicalize(state.path.as_ref().unwrap()).unwrap(),
- )
- .unwrap(),
+ uri: lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
language_id: "rust".to_string(), // TODO: hardcoded for now
version: 0,
text: String::from(&state.doc),
@@ -243,11 +241,12 @@ impl Client {
&mut self,
state: &helix_core::State,
) -> anyhow::Result<()> {
- self.notify::<lsp::notification::DidSaveTextDocument>(lsp::DidSaveTextDocumentParams {
- text_document: lsp::TextDocumentIdentifier::new(
- lsp::Url::from_file_path(state.path.as_ref().unwrap()).unwrap(),
+ self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
+ text_document: lsp::VersionedTextDocumentIdentifier::new(
+ lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
+ 0, // TODO: version
),
- text: None, // TODO?
+ content_changes: vec![], // TODO:
})
.await
}
diff --git a/helix-lsp/src/transport.rs b/helix-lsp/src/transport.rs
index c44ffa91..8915a925 100644
--- a/helix-lsp/src/transport.rs
+++ b/helix-lsp/src/transport.rs
@@ -24,7 +24,7 @@ pub(crate) enum Payload {
}
pub(crate) struct Transport {
- incoming: Sender<Message>,
+ incoming: Sender<Notification>, // TODO Notification | Call
outgoing: Receiver<Payload>,
pending_requests: HashMap<jsonrpc::Id, Sender<anyhow::Result<Value>>>,
@@ -39,7 +39,7 @@ impl Transport {
ex: &Executor,
reader: BufReader<ChildStdout>,
writer: BufWriter<ChildStdin>,
- ) -> (Receiver<Message>, Sender<Payload>) {
+ ) -> (Receiver<Notification>, Sender<Payload>) {
let (incoming, rx) = smol::channel::unbounded();
let (tx, outgoing) = smol::channel::unbounded();
@@ -111,7 +111,7 @@ impl Transport {
}
pub async fn send(&mut self, request: String) -> anyhow::Result<()> {
- println!("-> {}", request);
+ // println!("-> {}", request);
// send the headers
self.writer
@@ -132,11 +132,11 @@ impl Transport {
Message::Notification(jsonrpc::Notification { method, params, .. }) => {
let notification = Notification::parse(&method, params);
- println!("<- {} {:?}", method, notification);
- // dispatch
+ // println!("<- {} {:?}", method, notification);
+ self.incoming.send(notification).await?;
}
Message::Call(call) => {
- println!("<- {:?}", call);
+ // println!("<- {:?}", call);
// dispatch
}
_ => unreachable!(),
@@ -147,7 +147,7 @@ impl Transport {
pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> {
match output {
jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => {
- println!("<- {}", result);
+ // println!("<- {}", result);
let tx = self
.pending_requests
diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml
index aa91a095..db1edee9 100644
--- a/helix-term/Cargo.toml
+++ b/helix-term/Cargo.toml
@@ -24,3 +24,5 @@ num_cpus = "1.13"
tui = { git = "https://github.com/fdehau/tui-rs", default-features = false, features = ["crossterm"] }
crossterm = { version = "0.18", features = ["event-stream"] }
clap = { version = "3.0.0-beta.2 ", default-features = false, features = ["std", "cargo"] }
+
+futures-util = "0.3"
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index d65e7e2e..3d5b3459 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -1,6 +1,11 @@
use clap::ArgMatches as Args;
use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State};
-use helix_view::{commands, keymap, prompt::Prompt, Editor, Theme, View};
+use helix_view::{
+ commands,
+ keymap::{self, Keymaps},
+ prompt::Prompt,
+ Editor, Theme, View,
+};
use std::{
borrow::Cow,
@@ -31,14 +36,16 @@ const OFFSET: u16 = 6; // 5 linenr + 1 gutter
type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
-static EX: smol::Executor = smol::Executor::new();
-
const BASE_WIDTH: u16 = 30;
-pub struct Application {
+pub struct Application<'a> {
editor: Editor,
prompt: Option<Prompt>,
terminal: Renderer,
+
+ keymap: Keymaps,
+ executor: &'a smol::Executor<'a>,
+ lsp: helix_lsp::Client,
}
struct Renderer {
@@ -235,7 +242,7 @@ impl Renderer {
.set_string(1, self.size.1 - 2, mode, self.text_color);
}
- pub fn render_prompt(&mut self, view: &View, prompt: &Prompt) {
+ pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
// completion
if !prompt.completion.is_empty() {
// TODO: find out better way of clearing individual lines of the screen
@@ -254,7 +261,7 @@ impl Renderer {
}
self.surface.set_style(
Rect::new(0, self.size.1 - col_height - 2, self.size.0, col_height),
- view.theme.get("ui.statusline"),
+ theme.get("ui.statusline"),
);
for (i, command) in prompt.completion.iter().enumerate() {
let color = if prompt.completion_selection_index.is_some()
@@ -330,8 +337,8 @@ impl Renderer {
}
}
-impl Application {
- pub fn new(mut args: Args) -> Result<Self, Error> {
+impl<'a> Application<'a> {
+ pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Error> {
let terminal = Renderer::new()?;
let mut editor = Editor::new();
@@ -339,11 +346,18 @@ impl Application {
editor.open(file, terminal.size)?;
}
+ let lsp = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
+
let mut app = Self {
editor,
terminal,
// TODO; move to state
prompt: None,
+
+ //
+ keymap: keymap::default(),
+ executor,
+ lsp,
};
Ok(app)
@@ -361,7 +375,7 @@ impl Application {
if prompt.should_close {
self.prompt = None;
} else {
- self.terminal.render_prompt(view, prompt);
+ self.terminal.render_prompt(view, prompt, theme_ref);
}
}
}
@@ -375,7 +389,13 @@ impl Application {
pub async fn event_loop(&mut self) {
let mut reader = EventStream::new();
- let keymap = keymap::default();
+
+ // initialize lsp
+ let res = self.lsp.initialize().await;
+ let res = self
+ .lsp
+ .text_document_did_open(&self.editor.view().unwrap().state)
+ .await;
self.render();
@@ -384,126 +404,149 @@ impl Application {
break;
}
- // Handle key events
- match reader.next().await {
- Some(Ok(Event::Resize(width, height))) => {
- self.terminal.resize(width, height);
+ use futures_util::{select, FutureExt};
+ select! {
+ event = reader.next().fuse() => {
+ self.handle_terminal_events(event).await
+ }
+ notification = self.lsp.incoming.next().fuse() => {
+ self.handle_lsp_notification(notification).await
+ }
+ }
+ }
+ }
- // TODO: simplistic ensure cursor in view for now
- // TODO: loop over views
- if let Some(view) = self.editor.view_mut() {
- view.size = self.terminal.size;
- view.ensure_cursor_in_view()
- };
+ pub async fn handle_terminal_events(
+ &mut self,
+ event: Option<Result<Event, crossterm::ErrorKind>>,
+ ) {
+ // Handle key events
+ match event {
+ Some(Ok(Event::Resize(width, height))) => {
+ self.terminal.resize(width, height);
+
+ // TODO: simplistic ensure cursor in view for now
+ // TODO: loop over views
+ if let Some(view) = self.editor.view_mut() {
+ view.size = self.terminal.size;
+ view.ensure_cursor_in_view()
+ };
+
+ self.render();
+ }
+ Some(Ok(Event::Key(event))) => {
+ // if there's a prompt, it takes priority
+ if let Some(prompt) = &mut self.prompt {
+ self.prompt
+ .as_mut()
+ .unwrap()
+ .handle_input(event, &mut self.editor);
self.render();
- }
- Some(Ok(Event::Key(event))) => {
- // if there's a prompt, it takes priority
- if let Some(prompt) = &mut self.prompt {
- self.prompt
- .as_mut()
- .unwrap()
- .handle_input(event, &mut self.editor);
-
- self.render();
- } else if let Some(view) = self.editor.view_mut() {
- let keys = vec![event];
- // TODO: sequences (`gg`)
- // TODO: handle count other than 1
- match view.state.mode() {
- Mode::Insert => {
- if let Some(command) = keymap[&Mode::Insert].get(&keys) {
- command(view, 1);
- } else if let KeyEvent {
- code: KeyCode::Char(c),
- ..
- } = event
- {
- commands::insert::insert_char(view, c);
- }
- view.ensure_cursor_in_view();
+ } else if let Some(view) = self.editor.view_mut() {
+ let keys = vec![event];
+ // TODO: sequences (`gg`)
+ // TODO: handle count other than 1
+ match view.state.mode() {
+ Mode::Insert => {
+ if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
+ command(view, 1);
+ } else if let KeyEvent {
+ code: KeyCode::Char(c),
+ ..
+ } = event
+ {
+ commands::insert::insert_char(view, c);
}
- Mode::Normal => {
- if let &[KeyEvent {
- code: KeyCode::Char(':'),
- ..
- }] = keys.as_slice()
- {
- let prompt = Prompt::new(
- ":".to_owned(),
- |_input: &str| {
- // TODO: i need this duplicate list right now to avoid borrow checker issues
- let command_list = vec![
- String::from("q"),
- String::from("aaa"),
- String::from("bbb"),
- String::from("ccc"),
- String::from("ddd"),
- String::from("eee"),
- String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
- String::from("q"),
- String::from("aaa"),
- String::from("bbb"),
- String::from("ccc"),
- String::from("ddd"),
- String::from("eee"),
- String::from("q"),
- String::from("aaa"),
- String::from("bbb"),
- String::from("ccc"),
- String::from("ddd"),
- String::from("eee"),
- String::from("q"),
- String::from("aaa"),
- String::from("bbb"),
- String::from("ccc"),
- String::from("ddd"),
- String::from("eee"),
- String::from("q"),
- String::from("aaa"),
- String::from("bbb"),
- String::from("ccc"),
- String::from("ddd"),
- String::from("eee"),
- ];
- command_list
- .into_iter()
- .filter(|command| command.contains(_input))
- .collect()
- }, // completion
- |editor: &mut Editor, input: &str| match input {
- "q" => editor.should_close = true,
- _ => (),
- },
- );
-
- self.prompt = Some(prompt);
-
- // HAXX: special casing for command mode
- } else if let Some(command) = keymap[&Mode::Normal].get(&keys) {
- command(view, 1);
-
- // TODO: simplistic ensure cursor in view for now
- view.ensure_cursor_in_view();
- }
+ view.ensure_cursor_in_view();
+ }
+ Mode::Normal => {
+ if let &[KeyEvent {
+ code: KeyCode::Char(':'),
+ ..
+ }] = keys.as_slice()
+ {
+ let prompt = Prompt::new(
+ ":".to_owned(),
+ |_input: &str| {
+ // TODO: i need this duplicate list right now to avoid borrow checker issues
+ let command_list = vec![
+ String::from("q"),
+ String::from("aaa"),
+ String::from("bbb"),
+ String::from("ccc"),
+ String::from("ddd"),
+ String::from("eee"),
+ String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
+ String::from("q"),
+ String::from("aaa"),
+ String::from("bbb"),
+ String::from("ccc"),
+ String::from("ddd"),
+ String::from("eee"),
+ String::from("q"),
+ String::from("aaa"),
+ String::from("bbb"),
+ String::from("ccc"),
+ String::from("ddd"),
+ String::from("eee"),
+ String::from("q"),
+ String::from("aaa"),
+ String::from("bbb"),
+ String::from("ccc"),
+ String::from("ddd"),
+ String::from("eee"),
+ String::from("q"),
+ String::from("aaa"),
+ String::from("bbb"),
+ String::from("ccc"),
+ String::from("ddd"),
+ String::from("eee"),
+ ];
+ command_list
+ .into_iter()
+ .filter(|command| command.contains(_input))
+ .collect()
+ }, // completion
+ |editor: &mut Editor, input: &str| match input {
+ "q" => editor.should_close = true,
+ _ => (),
+ },
+ );
+
+ self.prompt = Some(prompt);
+
+ // HAXX: special casing for command mode
+ } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
+ command(view, 1);
+
+ // TODO: simplistic ensure cursor in view for now
+ view.ensure_cursor_in_view();
}
- mode => {
- if let Some(command) = keymap[&mode].get(&keys) {
- command(view, 1);
+ }
+ mode => {
+ if let Some(command) = self.keymap[&mode].get(&keys) {
+ command(view, 1);
- // TODO: simplistic ensure cursor in view for now
- view.ensure_cursor_in_view();
- }
+ // TODO: simplistic ensure cursor in view for now
+ view.ensure_cursor_in_view();
}
}
- self.render();
}
+ self.render();
}
- Some(Ok(Event::Mouse(_))) => (), // unhandled
- Some(Err(x)) => panic!(x),
- None => break,
}
+ Some(Ok(Event::Mouse(_))) => (), // unhandled
+ Some(Err(x)) => panic!(x),
+ None => panic!(),
+ };
+ }
+
+ pub async fn handle_lsp_notification(&mut self, notification: Option<helix_lsp::Notification>) {
+ use helix_lsp::Notification;
+ match notification {
+ Some(Notification::PublishDiagnostics(params)) => unimplemented!("{:?}", params),
+ _ => unreachable!(),
}
}
diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs
index 07f1ffff..de3a0175 100644
--- a/helix-term/src/main.rs
+++ b/helix-term/src/main.rs
@@ -26,15 +26,15 @@ fn main() -> Result<(), Error> {
std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>())));
}
- let mut lsp = helix_lsp::Client::start(&EX, "rust-analyzer", &[]);
+ // let mut lsp = helix_lsp::Client::start(&EX, "rust-analyzer", &[]);
smol::block_on(async {
- let res = lsp.initialize().await;
- let state = helix_core::State::load("test.rs".into(), &[]).unwrap();
- let res = lsp.text_document_did_open(&state).await;
- loop {}
+ // let res = lsp.initialize().await;
+ // let state = helix_core::State::load("test.rs".into(), &[]).unwrap();
+ // let res = lsp.text_document_did_open(&state).await;
+ // loop {}
- // Application::new(args).unwrap().run().await;
+ Application::new(args, &EX).unwrap().run().await;
});
Ok(())
diff --git a/helix-view/src/keymap.rs b/helix-view/src/keymap.rs
index 69e6cabb..82bdbe21 100644
--- a/helix-view/src/keymap.rs
+++ b/helix-view/src/keymap.rs
@@ -87,8 +87,8 @@ use std::collections::HashMap;
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
// TODO: could be trie based
-type Keymap = HashMap<Vec<Key>, Command>;
-type Keymaps = HashMap<state::Mode, Keymap>;
+pub type Keymap = HashMap<Vec<Key>, Command>;
+pub type Keymaps = HashMap<state::Mode, Keymap>;
macro_rules! key {
($ch:expr) => {
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index d2a7d556..817714c8 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -18,7 +18,7 @@ pub struct View {
pub first_line: usize,
pub size: (u16, u16),
- // TODO: Doc<> fields
+ // TODO: Doc fields
pub history: History,
}