mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 09:26:19 +04:00
Reroute LSP notification events into the main app event loop.
This commit is contained in:
parent
64b5b23315
commit
f9bfba4d96
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -472,6 +472,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"futures-util",
|
||||||
"helix-core",
|
"helix-core",
|
||||||
"helix-lsp",
|
"helix-lsp",
|
||||||
"helix-view",
|
"helix-view",
|
||||||
|
1
helix-core/src/diagnostic.rs
Normal file
1
helix-core/src/diagnostic.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub struct Diagnostic {}
|
@ -1,4 +1,5 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
mod diagnostic;
|
||||||
pub mod graphemes;
|
pub mod graphemes;
|
||||||
mod history;
|
mod history;
|
||||||
pub mod indent;
|
pub mod indent;
|
||||||
@ -22,6 +23,7 @@
|
|||||||
pub use selection::Selection;
|
pub use selection::Selection;
|
||||||
pub use syntax::Syntax;
|
pub use syntax::Syntax;
|
||||||
|
|
||||||
|
pub use diagnostic::Diagnostic;
|
||||||
pub use history::History;
|
pub use history::History;
|
||||||
pub use state::State;
|
pub use state::State;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
|
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
|
||||||
use crate::syntax::LOADER;
|
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 anyhow::Error;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -28,6 +28,8 @@ pub struct State {
|
|||||||
/// Pending changes since last history commit.
|
/// Pending changes since last history commit.
|
||||||
pub changes: ChangeSet,
|
pub changes: ChangeSet,
|
||||||
pub old_state: Option<(Rope, Selection)>,
|
pub old_state: Option<(Rope, Selection)>,
|
||||||
|
|
||||||
|
pub diagnostics: Vec<Diagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
@ -58,12 +60,13 @@ pub fn new(doc: Rope) -> Self {
|
|||||||
syntax: None,
|
syntax: None,
|
||||||
changes,
|
changes,
|
||||||
old_state,
|
old_state,
|
||||||
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: passing scopes here is awkward
|
// TODO: passing scopes here is awkward
|
||||||
pub fn load(path: PathBuf, scopes: &[String]) -> Result<Self, Error> {
|
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 _current_dir = env::current_dir()?;
|
||||||
|
|
||||||
let doc = Rope::from_reader(BufReader::new(File::open(path.clone())?))?;
|
let doc = Rope::from_reader(BufReader::new(File::open(path.clone())?))?;
|
||||||
@ -81,7 +84,8 @@ pub fn load(path: PathBuf, scopes: &[String]) -> Result<Self, Error> {
|
|||||||
state.syntax = Some(syntax);
|
state.syntax = Some(syntax);
|
||||||
};
|
};
|
||||||
|
|
||||||
state.path = Some(path);
|
// canonicalize path to absolute value
|
||||||
|
state.path = Some(std::fs::canonicalize(path)?);
|
||||||
|
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use transport::{Payload, Transport};
|
use transport::{Payload, Transport};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
// use std::collections::HashMap;
|
||||||
|
|
||||||
use jsonrpc_core as jsonrpc;
|
use jsonrpc_core as jsonrpc;
|
||||||
use lsp_types as lsp;
|
use lsp_types as lsp;
|
||||||
@ -32,10 +32,12 @@ enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
enum Notification {}
|
pub enum Notification {
|
||||||
|
PublishDiagnostics(lsp::PublishDiagnosticsParams),
|
||||||
|
}
|
||||||
|
|
||||||
impl Notification {
|
impl Notification {
|
||||||
pub fn parse(method: &str, params: jsonrpc::Params) {
|
pub fn parse(method: &str, params: jsonrpc::Params) -> Notification {
|
||||||
use lsp::notification::Notification as _;
|
use lsp::notification::Notification as _;
|
||||||
|
|
||||||
match method {
|
match method {
|
||||||
@ -44,11 +46,10 @@ pub fn parse(method: &str, params: jsonrpc::Params) {
|
|||||||
.parse()
|
.parse()
|
||||||
.expect("Failed to parse PublishDiagnostics params");
|
.expect("Failed to parse PublishDiagnostics params");
|
||||||
|
|
||||||
println!("{:?}", params);
|
|
||||||
|
|
||||||
// TODO: need to loop over diagnostics and distinguish them by URI
|
// 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>,
|
stderr: BufReader<ChildStderr>,
|
||||||
|
|
||||||
outgoing: Sender<Payload>,
|
outgoing: Sender<Payload>,
|
||||||
incoming: Receiver<Message>,
|
pub incoming: Receiver<Notification>,
|
||||||
|
|
||||||
pub request_counter: u64,
|
pub request_counter: u64,
|
||||||
|
|
||||||
capabilities: Option<lsp::ServerCapabilities>,
|
capabilities: Option<lsp::ServerCapabilities>,
|
||||||
// TODO: handle PublishDiagnostics Version
|
// TODO: handle PublishDiagnostics Version
|
||||||
diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
|
// diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@ -95,7 +96,7 @@ pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> Self {
|
|||||||
request_counter: 0,
|
request_counter: 0,
|
||||||
|
|
||||||
capabilities: None,
|
capabilities: None,
|
||||||
diagnostics: HashMap::new(),
|
// diagnostics: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,10 +227,7 @@ pub async fn text_document_did_open(
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::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(
|
uri: lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
|
||||||
std::fs::canonicalize(state.path.as_ref().unwrap()).unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
language_id: "rust".to_string(), // TODO: hardcoded for now
|
language_id: "rust".to_string(), // TODO: hardcoded for now
|
||||||
version: 0,
|
version: 0,
|
||||||
text: String::from(&state.doc),
|
text: String::from(&state.doc),
|
||||||
@ -243,11 +241,12 @@ pub async fn text_document_did_change(
|
|||||||
&mut self,
|
&mut self,
|
||||||
state: &helix_core::State,
|
state: &helix_core::State,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.notify::<lsp::notification::DidSaveTextDocument>(lsp::DidSaveTextDocumentParams {
|
self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
|
||||||
text_document: lsp::TextDocumentIdentifier::new(
|
text_document: lsp::VersionedTextDocumentIdentifier::new(
|
||||||
lsp::Url::from_file_path(state.path.as_ref().unwrap()).unwrap(),
|
lsp::Url::from_file_path(state.path().unwrap()).unwrap(),
|
||||||
|
0, // TODO: version
|
||||||
),
|
),
|
||||||
text: None, // TODO?
|
content_changes: vec![], // TODO:
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ pub(crate) enum Payload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Transport {
|
pub(crate) struct Transport {
|
||||||
incoming: Sender<Message>,
|
incoming: Sender<Notification>, // TODO Notification | Call
|
||||||
outgoing: Receiver<Payload>,
|
outgoing: Receiver<Payload>,
|
||||||
|
|
||||||
pending_requests: HashMap<jsonrpc::Id, Sender<anyhow::Result<Value>>>,
|
pending_requests: HashMap<jsonrpc::Id, Sender<anyhow::Result<Value>>>,
|
||||||
@ -39,7 +39,7 @@ pub fn start(
|
|||||||
ex: &Executor,
|
ex: &Executor,
|
||||||
reader: BufReader<ChildStdout>,
|
reader: BufReader<ChildStdout>,
|
||||||
writer: BufWriter<ChildStdin>,
|
writer: BufWriter<ChildStdin>,
|
||||||
) -> (Receiver<Message>, Sender<Payload>) {
|
) -> (Receiver<Notification>, Sender<Payload>) {
|
||||||
let (incoming, rx) = smol::channel::unbounded();
|
let (incoming, rx) = smol::channel::unbounded();
|
||||||
let (tx, outgoing) = smol::channel::unbounded();
|
let (tx, outgoing) = smol::channel::unbounded();
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ pub async fn send_payload(&mut self, payload: Payload) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&mut self, request: String) -> anyhow::Result<()> {
|
pub async fn send(&mut self, request: String) -> anyhow::Result<()> {
|
||||||
println!("-> {}", request);
|
// println!("-> {}", request);
|
||||||
|
|
||||||
// send the headers
|
// send the headers
|
||||||
self.writer
|
self.writer
|
||||||
@ -132,11 +132,11 @@ pub async fn recv_msg(&mut self, msg: Message) -> anyhow::Result<()> {
|
|||||||
Message::Notification(jsonrpc::Notification { method, params, .. }) => {
|
Message::Notification(jsonrpc::Notification { method, params, .. }) => {
|
||||||
let notification = Notification::parse(&method, params);
|
let notification = Notification::parse(&method, params);
|
||||||
|
|
||||||
println!("<- {} {:?}", method, notification);
|
// println!("<- {} {:?}", method, notification);
|
||||||
// dispatch
|
self.incoming.send(notification).await?;
|
||||||
}
|
}
|
||||||
Message::Call(call) => {
|
Message::Call(call) => {
|
||||||
println!("<- {:?}", call);
|
// println!("<- {:?}", call);
|
||||||
// dispatch
|
// dispatch
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -147,7 +147,7 @@ pub async fn recv_msg(&mut self, msg: Message) -> anyhow::Result<()> {
|
|||||||
pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> {
|
pub async fn recv_response(&mut self, output: jsonrpc::Output) -> anyhow::Result<()> {
|
||||||
match output {
|
match output {
|
||||||
jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => {
|
jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => {
|
||||||
println!("<- {}", result);
|
// println!("<- {}", result);
|
||||||
|
|
||||||
let tx = self
|
let tx = self
|
||||||
.pending_requests
|
.pending_requests
|
||||||
|
@ -24,3 +24,5 @@ num_cpus = "1.13"
|
|||||||
tui = { git = "https://github.com/fdehau/tui-rs", default-features = false, features = ["crossterm"] }
|
tui = { git = "https://github.com/fdehau/tui-rs", default-features = false, features = ["crossterm"] }
|
||||||
crossterm = { version = "0.18", features = ["event-stream"] }
|
crossterm = { version = "0.18", features = ["event-stream"] }
|
||||||
clap = { version = "3.0.0-beta.2 ", default-features = false, features = ["std", "cargo"] }
|
clap = { version = "3.0.0-beta.2 ", default-features = false, features = ["std", "cargo"] }
|
||||||
|
|
||||||
|
futures-util = "0.3"
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
use clap::ArgMatches as Args;
|
use clap::ArgMatches as Args;
|
||||||
use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State};
|
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::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -31,14 +36,16 @@
|
|||||||
|
|
||||||
type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
|
type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
|
||||||
|
|
||||||
static EX: smol::Executor = smol::Executor::new();
|
|
||||||
|
|
||||||
const BASE_WIDTH: u16 = 30;
|
const BASE_WIDTH: u16 = 30;
|
||||||
|
|
||||||
pub struct Application {
|
pub struct Application<'a> {
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
prompt: Option<Prompt>,
|
prompt: Option<Prompt>,
|
||||||
terminal: Renderer,
|
terminal: Renderer,
|
||||||
|
|
||||||
|
keymap: Keymaps,
|
||||||
|
executor: &'a smol::Executor<'a>,
|
||||||
|
lsp: helix_lsp::Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Renderer {
|
struct Renderer {
|
||||||
@ -235,7 +242,7 @@ pub fn render_statusline(&mut self, view: &View, theme: &Theme) {
|
|||||||
.set_string(1, self.size.1 - 2, mode, self.text_color);
|
.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
|
// completion
|
||||||
if !prompt.completion.is_empty() {
|
if !prompt.completion.is_empty() {
|
||||||
// TODO: find out better way of clearing individual lines of the screen
|
// TODO: find out better way of clearing individual lines of the screen
|
||||||
@ -254,7 +261,7 @@ pub fn render_prompt(&mut self, view: &View, prompt: &Prompt) {
|
|||||||
}
|
}
|
||||||
self.surface.set_style(
|
self.surface.set_style(
|
||||||
Rect::new(0, self.size.1 - col_height - 2, self.size.0, col_height),
|
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() {
|
for (i, command) in prompt.completion.iter().enumerate() {
|
||||||
let color = if prompt.completion_selection_index.is_some()
|
let color = if prompt.completion_selection_index.is_some()
|
||||||
@ -330,8 +337,8 @@ pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl<'a> Application<'a> {
|
||||||
pub fn new(mut args: Args) -> Result<Self, Error> {
|
pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Error> {
|
||||||
let terminal = Renderer::new()?;
|
let terminal = Renderer::new()?;
|
||||||
let mut editor = Editor::new();
|
let mut editor = Editor::new();
|
||||||
|
|
||||||
@ -339,11 +346,18 @@ pub fn new(mut args: Args) -> Result<Self, Error> {
|
|||||||
editor.open(file, terminal.size)?;
|
editor.open(file, terminal.size)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lsp = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
|
||||||
|
|
||||||
let mut app = Self {
|
let mut app = Self {
|
||||||
editor,
|
editor,
|
||||||
terminal,
|
terminal,
|
||||||
// TODO; move to state
|
// TODO; move to state
|
||||||
prompt: None,
|
prompt: None,
|
||||||
|
|
||||||
|
//
|
||||||
|
keymap: keymap::default(),
|
||||||
|
executor,
|
||||||
|
lsp,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(app)
|
Ok(app)
|
||||||
@ -361,7 +375,7 @@ fn render(&mut self) {
|
|||||||
if prompt.should_close {
|
if prompt.should_close {
|
||||||
self.prompt = None;
|
self.prompt = None;
|
||||||
} else {
|
} else {
|
||||||
self.terminal.render_prompt(view, prompt);
|
self.terminal.render_prompt(view, prompt, theme_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -375,7 +389,13 @@ fn render(&mut self) {
|
|||||||
|
|
||||||
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 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();
|
self.render();
|
||||||
|
|
||||||
@ -384,126 +404,149 @@ pub async fn event_loop(&mut self) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle key events
|
use futures_util::{select, FutureExt};
|
||||||
match reader.next().await {
|
select! {
|
||||||
Some(Ok(Event::Resize(width, height))) => {
|
event = reader.next().fuse() => {
|
||||||
self.terminal.resize(width, height);
|
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
|
pub async fn handle_terminal_events(
|
||||||
// TODO: loop over views
|
&mut self,
|
||||||
if let Some(view) = self.editor.view_mut() {
|
event: Option<Result<Event, crossterm::ErrorKind>>,
|
||||||
view.size = self.terminal.size;
|
) {
|
||||||
view.ensure_cursor_in_view()
|
// 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();
|
self.render();
|
||||||
}
|
} else if let Some(view) = self.editor.view_mut() {
|
||||||
Some(Ok(Event::Key(event))) => {
|
let keys = vec![event];
|
||||||
// if there's a prompt, it takes priority
|
// TODO: sequences (`gg`)
|
||||||
if let Some(prompt) = &mut self.prompt {
|
// TODO: handle count other than 1
|
||||||
self.prompt
|
match view.state.mode() {
|
||||||
.as_mut()
|
Mode::Insert => {
|
||||||
.unwrap()
|
if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
|
||||||
.handle_input(event, &mut self.editor);
|
command(view, 1);
|
||||||
|
} else if let KeyEvent {
|
||||||
|
code: KeyCode::Char(c),
|
||||||
|
..
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
commands::insert::insert_char(view, c);
|
||||||
|
}
|
||||||
|
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.render();
|
self.prompt = Some(prompt);
|
||||||
} else if let Some(view) = self.editor.view_mut() {
|
|
||||||
let keys = vec![event];
|
// HAXX: special casing for command mode
|
||||||
// TODO: sequences (`gg`)
|
} else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
|
||||||
// TODO: handle count other than 1
|
command(view, 1);
|
||||||
match view.state.mode() {
|
|
||||||
Mode::Insert => {
|
// TODO: simplistic ensure cursor in view for now
|
||||||
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();
|
view.ensure_cursor_in_view();
|
||||||
}
|
}
|
||||||
Mode::Normal => {
|
}
|
||||||
if let &[KeyEvent {
|
mode => {
|
||||||
code: KeyCode::Char(':'),
|
if let Some(command) = self.keymap[&mode].get(&keys) {
|
||||||
..
|
command(view, 1);
|
||||||
}] = 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);
|
// TODO: simplistic ensure cursor in view for now
|
||||||
|
view.ensure_cursor_in_view();
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mode => {
|
|
||||||
if let Some(command) = keymap[&mode].get(&keys) {
|
|
||||||
command(view, 1);
|
|
||||||
|
|
||||||
// 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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,15 +26,15 @@ fn main() -> Result<(), Error> {
|
|||||||
std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>())));
|
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 {
|
smol::block_on(async {
|
||||||
let res = lsp.initialize().await;
|
// let res = lsp.initialize().await;
|
||||||
let state = helix_core::State::load("test.rs".into(), &[]).unwrap();
|
// let state = helix_core::State::load("test.rs".into(), &[]).unwrap();
|
||||||
let res = lsp.text_document_did_open(&state).await;
|
// let res = lsp.text_document_did_open(&state).await;
|
||||||
loop {}
|
// loop {}
|
||||||
|
|
||||||
// Application::new(args).unwrap().run().await;
|
Application::new(args, &EX).unwrap().run().await;
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -87,8 +87,8 @@
|
|||||||
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
|
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
|
||||||
|
|
||||||
// TODO: could be trie based
|
// TODO: could be trie based
|
||||||
type Keymap = HashMap<Vec<Key>, Command>;
|
pub type Keymap = HashMap<Vec<Key>, Command>;
|
||||||
type Keymaps = HashMap<state::Mode, Keymap>;
|
pub type Keymaps = HashMap<state::Mode, Keymap>;
|
||||||
|
|
||||||
macro_rules! key {
|
macro_rules! key {
|
||||||
($ch:expr) => {
|
($ch:expr) => {
|
||||||
|
@ -18,7 +18,7 @@ pub struct View {
|
|||||||
pub first_line: usize,
|
pub first_line: usize,
|
||||||
pub size: (u16, u16),
|
pub size: (u16, u16),
|
||||||
|
|
||||||
// TODO: Doc<> fields
|
// TODO: Doc fields
|
||||||
pub history: History,
|
pub history: History,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user