@@ -1,6 +1,11 @@
11use clap::ArgMatches as Args;
22use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State};
3−use helix_view::{commands, keymap, prompt::Prompt, Editor, Theme, View};
3+use helix_view::{
4+ commands,
5+ keymap::{self, Keymaps},
6+ prompt::Prompt,
7+ Editor, Theme, View,
8+};
49
510use std::{
611 borrow::Cow,
@@ -31,14 +36,16 @@ const OFFSET: u16 = 6; // 5 linenr + 1 gutter
3136
3237type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
3338
34−static EX: smol::Executor = smol::Executor::new();
35−
3639const BASE_WIDTH: u16 = 30;
3740
38−pub struct Application {
41+pub struct Application<'a> {
3942 editor: Editor,
4043 prompt: Option<Prompt>,
4144 terminal: Renderer,
45+
46+ keymap: Keymaps,
47+ executor: &'a smol::Executor<'a>,
48+ lsp: helix_lsp::Client,
4249}
4350
4451struct Renderer {
@@ -235,7 +242,7 @@ impl Renderer {
235242 .set_string(1, self.size.1 - 2, mode, self.text_color);
236243 }
237244
238− pub fn render_prompt(&mut self, view: &View, prompt: &Prompt) {
245+ pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
239246 // completion
240247 if !prompt.completion.is_empty() {
241248 // TODO: find out better way of clearing individual lines of the screen
@@ -254,7 +261,7 @@ impl Renderer {
254261 }
255262 self.surface.set_style(
256263 Rect::new(0, self.size.1 - col_height - 2, self.size.0, col_height),
257− view.theme.get("ui.statusline"),
264+ theme.get("ui.statusline"),
258265 );
259266 for (i, command) in prompt.completion.iter().enumerate() {
260267 let color = if prompt.completion_selection_index.is_some()
@@ -330,8 +337,8 @@ impl Renderer {
330337 }
331338}
332339
333−impl Application {
334− pub fn new(mut args: Args) -> Result<Self, Error> {
340+impl<'a> Application<'a> {
341+ pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Error> {
335342 let terminal = Renderer::new()?;
336343 let mut editor = Editor::new();
337344
@@ -339,11 +346,18 @@ impl Application {
339346 editor.open(file, terminal.size)?;
340347 }
341348
349+ let lsp = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
350+
342351 let mut app = Self {
343352 editor,
344353 terminal,
345354 // TODO; move to state
346355 prompt: None,
356+
357+ //
358+ keymap: keymap::default(),
359+ executor,
360+ lsp,
347361 };
348362
349363 Ok(app)
@@ -361,7 +375,7 @@ impl Application {
361375 if prompt.should_close {
362376 self.prompt = None;
363377 } else {
364− self.terminal.render_prompt(view, prompt);
378+ self.terminal.render_prompt(view, prompt, theme_ref);
365379 }
366380 }
367381 }
@@ -375,7 +389,13 @@ impl Application {
375389
376390 pub async fn event_loop(&mut self) {
377391 let mut reader = EventStream::new();
378− let keymap = keymap::default();
392+
393+ // initialize lsp
394+ let res = self.lsp.initialize().await;
395+ let res = self
396+ .lsp
397+ .text_document_did_open(&self.editor.view().unwrap().state)
398+ .await;
379399
380400 self.render();
381401
@@ -384,126 +404,149 @@ impl Application {
384404 break;
385405 }
386406
387− // Handle key events
388− match reader.next().await {
389− Some(Ok(Event::Resize(width, height))) => {
390− self.terminal.resize(width, height);
407+ use futures_util::{select, FutureExt};
408+ select! {
409+ event = reader.next().fuse() => {
410+ self.handle_terminal_events(event).await
411+ }
412+ notification = self.lsp.incoming.next().fuse() => {
413+ self.handle_lsp_notification(notification).await
414+ }
415+ }
416+ }
417+ }
391418
392− // TODO: simplistic ensure cursor in view for now
393− // TODO: loop over views
394− if let Some(view) = self.editor.view_mut() {
395− view.size = self.terminal.size;
396− view.ensure_cursor_in_view()
397− };
419+ pub async fn handle_terminal_events(
420+ &mut self,
421+ event: Option<Result<Event, crossterm::ErrorKind>>,
422+ ) {
423+ // Handle key events
424+ match event {
425+ Some(Ok(Event::Resize(width, height))) => {
426+ self.terminal.resize(width, height);
427+
428+ // TODO: simplistic ensure cursor in view for now
429+ // TODO: loop over views
430+ if let Some(view) = self.editor.view_mut() {
431+ view.size = self.terminal.size;
432+ view.ensure_cursor_in_view()
433+ };
434+
435+ self.render();
436+ }
437+ Some(Ok(Event::Key(event))) => {
438+ // if there's a prompt, it takes priority
439+ if let Some(prompt) = &mut self.prompt {
440+ self.prompt
441+ .as_mut()
442+ .unwrap()
443+ .handle_input(event, &mut self.editor);
398444
399445 self.render();
400− }
401− Some(Ok(Event::Key(event))) => {
402− // if there's a prompt, it takes priority
403− if let Some(prompt) = &mut self.prompt {
404− self.prompt
405− .as_mut()
406− .unwrap()
407− .handle_input(event, &mut self.editor);
408−
409− self.render();
410− } else if let Some(view) = self.editor.view_mut() {
411− let keys = vec![event];
412− // TODO: sequences (`gg`)
413− // TODO: handle count other than 1
414− match view.state.mode() {
415− Mode::Insert => {
416− if let Some(command) = keymap[&Mode::Insert].get(&keys) {
417− command(view, 1);
418− } else if let KeyEvent {
419− code: KeyCode::Char(c),
420− ..
421− } = event
422− {
423− commands::insert::insert_char(view, c);
424− }
425− view.ensure_cursor_in_view();
446+ } else if let Some(view) = self.editor.view_mut() {
447+ let keys = vec![event];
448+ // TODO: sequences (`gg`)
449+ // TODO: handle count other than 1
450+ match view.state.mode() {
451+ Mode::Insert => {
452+ if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
453+ command(view, 1);
454+ } else if let KeyEvent {
455+ code: KeyCode::Char(c),
456+ ..
457+ } = event
458+ {
459+ commands::insert::insert_char(view, c);
426460 }
427− Mode::Normal => {
428− if let &[KeyEvent {
429− code: KeyCode::Char(':'),
430− ..
431− }] = keys.as_slice()
432− {
433− let prompt = Prompt::new(
434− ":".to_owned(),
435− |_input: &str| {
436− // TODO: i need this duplicate list right now to avoid borrow checker issues
437− let command_list = vec![
438− String::from("q"),
439− String::from("aaa"),
440− String::from("bbb"),
441− String::from("ccc"),
442− String::from("ddd"),
443− String::from("eee"),
444− String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
445− String::from("q"),
446− String::from("aaa"),
447− String::from("bbb"),
448− String::from("ccc"),
449− String::from("ddd"),
450− String::from("eee"),
451− String::from("q"),
452− String::from("aaa"),
453− String::from("bbb"),
454− String::from("ccc"),
455− String::from("ddd"),
456− String::from("eee"),
457− String::from("q"),
458− String::from("aaa"),
459− String::from("bbb"),
460− String::from("ccc"),
461− String::from("ddd"),
462− String::from("eee"),
463− String::from("q"),
464− String::from("aaa"),
465− String::from("bbb"),
466− String::from("ccc"),
467− String::from("ddd"),
468− String::from("eee"),
469− ];
470− command_list
471− .into_iter()
472− .filter(|command| command.contains(_input))
473− .collect()
474− }, // completion
475− |editor: &mut Editor, input: &str| match input {
476− "q" => editor.should_close = true,
477− _ => (),
478− },
479− );
480−
481− self.prompt = Some(prompt);
482−
483− // HAXX: special casing for command mode
484− } else if let Some(command) = keymap[&Mode::Normal].get(&keys) {
485− command(view, 1);
486−
487− // TODO: simplistic ensure cursor in view for now
488− view.ensure_cursor_in_view();
489− }
461+ view.ensure_cursor_in_view();
462+ }
463+ Mode::Normal => {
464+ if let &[KeyEvent {
465+ code: KeyCode::Char(':'),
466+ ..
467+ }] = keys.as_slice()
468+ {
469+ let prompt = Prompt::new(
470+ ":".to_owned(),
471+ |_input: &str| {
472+ // TODO: i need this duplicate list right now to avoid borrow checker issues
473+ let command_list = vec![
474+ String::from("q"),
475+ String::from("aaa"),
476+ String::from("bbb"),
477+ String::from("ccc"),
478+ String::from("ddd"),
479+ String::from("eee"),
480+ String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
481+ String::from("q"),
482+ String::from("aaa"),
483+ String::from("bbb"),
484+ String::from("ccc"),
485+ String::from("ddd"),
486+ String::from("eee"),
487+ String::from("q"),
488+ String::from("aaa"),
489+ String::from("bbb"),
490+ String::from("ccc"),
491+ String::from("ddd"),
492+ String::from("eee"),
493+ String::from("q"),
494+ String::from("aaa"),
495+ String::from("bbb"),
496+ String::from("ccc"),
497+ String::from("ddd"),
498+ String::from("eee"),
499+ String::from("q"),
500+ String::from("aaa"),
501+ String::from("bbb"),
502+ String::from("ccc"),
503+ String::from("ddd"),
504+ String::from("eee"),
505+ ];
506+ command_list
507+ .into_iter()
508+ .filter(|command| command.contains(_input))
509+ .collect()
510+ }, // completion
511+ |editor: &mut Editor, input: &str| match input {
512+ "q" => editor.should_close = true,
513+ _ => (),
514+ },
515+ );
516+
517+ self.prompt = Some(prompt);
518+
519+ // HAXX: special casing for command mode
520+ } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
521+ command(view, 1);
522+
523+ // TODO: simplistic ensure cursor in view for now
524+ view.ensure_cursor_in_view();
490525 }
491− mode => {
492− if let Some(command) = keymap[&mode].get(&keys) {
493− command(view, 1);
526+ }
527+ mode => {
528+ if let Some(command) = self.keymap[&mode].get(&keys) {
529+ command(view, 1);
494530
495− // TODO: simplistic ensure cursor in view for now
496− view.ensure_cursor_in_view();
497− }
531+ // TODO: simplistic ensure cursor in view for now
532+ view.ensure_cursor_in_view();
498533 }
499534 }
500− self.render();
501535 }
536+ self.render();
502537 }
503− Some(Ok(Event::Mouse(_))) => (), // unhandled
504− Some(Err(x)) => panic!(x),
505− None => break,
506538 }
539+ Some(Ok(Event::Mouse(_))) => (), // unhandled
540+ Some(Err(x)) => panic!(x),
541+ None => panic!(),
542+ };
543+ }
544+
545+ pub async fn handle_lsp_notification(&mut self, notification: Option<helix_lsp::Notification>) {
546+ use helix_lsp::Notification;
547+ match notification {
548+ Some(Notification::PublishDiagnostics(params)) => unimplemented!("{:?}", params),
549+ _ => unreachable!(),
507550 }
508551 }
509552