@@ -8,6 +8,8 @@ use helix_view::{
88 Document, Editor, Theme, View,
99};
1010
11+use crate::compositor::{Component, Compositor};
12+
1113use log::{debug, info};
1214
1315use std::{
@@ -35,23 +37,21 @@ use tui::{
3537 style::{Color, Modifier, Style},
3638};
3739
38−const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
39−
4040type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
4141
4242const BASE_WIDTH: u16 = 30;
4343
4444pub struct Application<'a> {
45− editor: Editor,
4645 prompt: Option<Prompt>,
47− terminal: Renderer,
4846
49− keymap: Keymaps,
47+ compositor: Compositor,
48+ renderer: Renderer,
49+
5050 executor: &'a smol::Executor<'a>,
5151 language_server: helix_lsp::Client,
5252}
5353
54−struct Renderer {
54+pub(crate) struct Renderer {
5555 size: (u16, u16),
5656 terminal: Terminal,
5757 surface: Surface,
@@ -92,7 +92,6 @@ impl Renderer {
9292 // TODO: ideally not &mut View but highlights require it because of cursor cache
9393 pub fn render_buffer(&mut self, view: &mut View, viewport: Rect, theme: &Theme) {
9494 let area = Rect::new(0, 0, self.size.0, self.size.1);
95− self.surface.reset(); // reset is faster than allocating new empty surface
9695
9796 // clear with background color
9897 self.surface.set_style(area, theme.get("ui.background"));
@@ -221,8 +220,12 @@ impl Renderer {
221220
222221 // TODO: paint cursor heads except primary
223222
224− self.surface
225− .set_string(OFFSET + visual_x, line, grapheme, style);
223+ self.surface.set_string(
224+ viewport.x + visual_x,
225+ viewport.y + line,
226+ grapheme,
227+ style,
228+ );
226229
227230 visual_x += width;
228231 }
@@ -321,7 +324,7 @@ impl Renderer {
321324 .set_string(2, self.size.1 - 1, &prompt.line, self.text_color);
322325 }
323326
324− pub fn draw(&mut self) {
327+ pub fn draw_and_swap(&mut self) {
325328 use tui::backend::Backend;
326329 // TODO: theres probably a better place for this
327330 self.terminal
@@ -363,112 +366,40 @@ impl Renderer {
363366 }
364367}
365368
366−impl<'a> Application<'a> {
367− pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Error> {
368− let terminal = Renderer::new()?;
369− let mut editor = Editor::new();
370−
371− if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
372− editor.open(file, terminal.size)?;
373− }
374−
375− let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
369+struct EditorView {
370+ editor: Editor,
371+ prompt: Option<Prompt>, // TODO: this is None for now, make a layer
372+ keymap: Keymaps,
373+}
376374
377− let mut app = Self {
375+impl EditorView {
376+ fn new(editor: Editor) -> Self {
377+ Self {
378378 editor,
379− terminal,
380− // TODO; move to state
381379 prompt: None,
382−
383− //
384380 keymap: keymap::default(),
385− executor,
386− language_server,
387− };
388−
389− Ok(app)
390− }
391−
392− fn render(&mut self) {
393− let viewport = Rect::new(OFFSET, 0, self.terminal.size.0, self.terminal.size.1 - 2); // - 2 for statusline and prompt
394−
395− // SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow
396− // theme. Theme is immutable mutating view won't disrupt theme_ref.
397− let theme_ref = unsafe { &*(&self.editor.theme as *const Theme) };
398− if let Some(view) = self.editor.view_mut() {
399− self.terminal.render_view(view, viewport, theme_ref);
400− if let Some(prompt) = &self.prompt {
401− if prompt.should_close {
402− self.prompt = None;
403− } else {
404− self.terminal.render_prompt(view, prompt, theme_ref);
405− }
406− }
407− }
408−
409− self.terminal.draw();
410−
411− // TODO: drop unwrap
412− self.terminal
413− .render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
414− }
415−
416− pub async fn event_loop(&mut self) {
417− let mut reader = EventStream::new();
418−
419− // initialize lsp
420− self.language_server.initialize().await.unwrap();
421− self.language_server
422− .text_document_did_open(&self.editor.view().unwrap().doc)
423− .await
424− .unwrap();
425−
426− self.render();
427−
428− loop {
429− if self.editor.should_close {
430− break;
431− }
432−
433− use futures_util::{select, FutureExt};
434− select! {
435− event = reader.next().fuse() => {
436− self.handle_terminal_events(event).await
437− }
438− call = self.language_server.incoming.next().fuse() => {
439− self.handle_language_server_message(call).await
440− }
441− }
442381 }
443382 }
383+}
444384
445− pub async fn handle_terminal_events(
446− &mut self,
447− event: Option<Result<Event, crossterm::ErrorKind>>,
448− ) {
449− // Handle key events
385+impl Component for EditorView {
386+ fn handle_event(&mut self, event: Event, executor: &smol::Executor) -> bool {
450387 match event {
451− Some(Ok(Event::Resize(width, height))) => {
452− self.terminal.resize(width, height);
453−
388+ Event::Resize(width, height) => {
454389 // TODO: simplistic ensure cursor in view for now
455390 // TODO: loop over views
456391 if let Some(view) = self.editor.view_mut() {
457− view.size = self.terminal.size;
392+ view.size = (width, height);
458393 view.ensure_cursor_in_view()
459394 };
460−
461− self.render();
462395 }
463− Some(Ok(Event::Key(event))) => {
396+ Event::Key(event) => {
464397 // if there's a prompt, it takes priority
465398 if let Some(prompt) = &mut self.prompt {
466399 self.prompt
467400 .as_mut()
468401 .unwrap()
469402 .handle_input(event, &mut self.editor);
470−
471− self.render();
472403 } else if let Some(view) = self.editor.view_mut() {
473404 let keys = vec![event];
474405 // TODO: sequences (`gg`)
@@ -478,7 +409,7 @@ impl<'a> Application<'a> {
478409 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
479410 let mut cx = helix_view::commands::Context {
480411 view,
481− executor: self.executor,
412+ executor: executor,
482413 count: 1,
483414 };
484415
@@ -490,7 +421,7 @@ impl<'a> Application<'a> {
490421 {
491422 let mut cx = helix_view::commands::Context {
492423 view,
493− executor: self.executor,
424+ executor: executor,
494425 count: 1,
495426 };
496427 commands::insert::insert_char(&mut cx, c);
@@ -557,7 +488,7 @@ impl<'a> Application<'a> {
557488 } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
558489 let mut cx = helix_view::commands::Context {
559490 view,
560− executor: self.executor,
491+ executor: executor,
561492 count: 1,
562493 };
563494 command(&mut cx);
@@ -570,7 +501,7 @@ impl<'a> Application<'a> {
570501 if let Some(command) = self.keymap[&mode].get(&keys) {
571502 let mut cx = helix_view::commands::Context {
572503 view,
573− executor: self.executor,
504+ executor: executor,
574505 count: 1,
575506 };
576507 command(&mut cx);
@@ -580,10 +511,119 @@ impl<'a> Application<'a> {
580511 }
581512 }
582513 }
583− self.render();
584514 }
585515 }
586− Some(Ok(Event::Mouse(_))) => (), // unhandled
516+ Event::Mouse(_) => (),
517+ }
518+
519+ true
520+ }
521+ fn render(&mut self, renderer: &mut Renderer) {
522+ const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
523+ let viewport = Rect::new(OFFSET, 0, renderer.size.0, renderer.size.1 - 2); // - 2 for statusline and prompt
524+
525+ // SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow
526+ // theme. Theme is immutable mutating view won't disrupt theme_ref.
527+ let theme_ref = unsafe { &*(&self.editor.theme as *const Theme) };
528+ if let Some(view) = self.editor.view_mut() {
529+ renderer.render_view(view, viewport, theme_ref);
530+ if let Some(prompt) = &self.prompt {
531+ if prompt.should_close {
532+ self.prompt = None;
533+ } else {
534+ renderer.render_prompt(view, prompt, theme_ref);
535+ }
536+ }
537+ }
538+
539+ // TODO: drop unwrap
540+ renderer.render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
541+ }
542+}
543+
544+impl<'a> Application<'a> {
545+ pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Error> {
546+ let renderer = Renderer::new()?;
547+ let mut editor = Editor::new();
548+
549+ if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
550+ editor.open(file, renderer.size)?;
551+ }
552+
553+ let mut compositor = Compositor::new();
554+ compositor.push(Box::new(EditorView::new(editor)));
555+
556+ let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
557+
558+ let mut app = Self {
559+ renderer,
560+ // TODO; move to state
561+ compositor,
562+ prompt: None,
563+
564+ executor,
565+ language_server,
566+ };
567+
568+ Ok(app)
569+ }
570+
571+ fn render(&mut self) {
572+ // v2:
573+ self.renderer.surface.reset(); // reset is faster than allocating new empty surface
574+ self.compositor.render(&mut self.renderer); // viewport,
575+ self.renderer.draw_and_swap();
576+ }
577+
578+ pub async fn event_loop(&mut self) {
579+ let mut reader = EventStream::new();
580+
581+ // initialize lsp
582+ self.language_server.initialize().await.unwrap();
583+ // TODO: temp
584+ // self.language_server
585+ // .text_document_did_open(&self.editor.view().unwrap().doc)
586+ // .await
587+ // .unwrap();
588+
589+ self.render();
590+
591+ loop {
592+ // TODO:
593+ // if self.editor.should_close {
594+ // break;
595+ // }
596+
597+ use futures_util::{select, FutureExt};
598+ select! {
599+ event = reader.next().fuse() => {
600+ self.handle_terminal_events(event)
601+ }
602+ call = self.language_server.incoming.next().fuse() => {
603+ self.handle_language_server_message(call).await
604+ }
605+ }
606+ }
607+ }
608+
609+ pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
610+ // Handle key events
611+ match event {
612+ Some(Ok(Event::Resize(width, height))) => {
613+ self.renderer.resize(width, height);
614+
615+ // TODO: use the response
616+ self.compositor
617+ .handle_event(Event::Resize(width, height), self.executor);
618+
619+ self.render();
620+ }
621+ Some(Ok(event)) => {
622+ // TODO: use the response
623+ self.compositor.handle_event(event, self.executor);
624+
625+ self.render();
626+ }
587627 Some(Err(x)) => panic!(x),
588628 None => panic!(),
589629 };
@@ -599,11 +639,13 @@ impl<'a> Application<'a> {
599639 match notification {
600640 Notification::PublishDiagnostics(params) => {
601641 let path = Some(params.uri.to_file_path().unwrap());
602− let view = self
603− .editor
604− .views
605− .iter_mut()
606− .find(|view| view.doc.path == path);
642+ let view: Option<&mut helix_view::View> = None;
643+ // TODO:
644+ // let view = self
645+ // .editor
646+ // .views
647+ // .iter_mut()
648+ // .find(|view| view.doc.path == path);
607649
608650 if let Some(view) = view {
609651 let doc = view.doc.text().slice(..);