@@ -8,6 +8,8 @@ use helix_view::{
8 Document, Editor, Theme, View,
8 Document, Editor, Theme, View,
11+use crate::compositor::{Component, Compositor};
11use log::{debug, info};
13use log::{debug, info};
@@ -35,23 +37,21 @@ use tui::{
35 style::{Color, Modifier, Style},
37 style::{Color, Modifier, Style},
38−const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
40type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
40type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
42const BASE_WIDTH: u16 = 30;
42const BASE_WIDTH: u16 = 30;
44pub struct Application<'a> {
44pub struct Application<'a> {
46 prompt: Option<Prompt>,
45 prompt: Option<Prompt>,
49− keymap: Keymaps,
47+ compositor: Compositor,
50 executor: &'a smol::Executor<'a>,
50 executor: &'a smol::Executor<'a>,
51 language_server: helix_lsp::Client,
51 language_server: helix_lsp::Client,
54−struct Renderer {
54+pub(crate) struct Renderer {
55 size: (u16, u16),
55 size: (u16, u16),
56 terminal: Terminal,
56 terminal: Terminal,
57 surface: Surface,
57 surface: Surface,
@@ -92,7 +92,6 @@ impl Renderer {
92 // TODO: ideally not &mut View but highlights require it because of cursor cache
92 // TODO: ideally not &mut View but highlights require it because of cursor cache
93 pub fn render_buffer(&mut self, view: &mut View, viewport: Rect, theme: &Theme) {
93 pub fn render_buffer(&mut self, view: &mut View, viewport: Rect, theme: &Theme) {
94 let area = Rect::new(0, 0, self.size.0, self.size.1);
94 let area = Rect::new(0, 0, self.size.0, self.size.1);
95− self.surface.reset(); // reset is faster than allocating new empty surface
97 // clear with background color
96 // clear with background color
98 self.surface.set_style(area, theme.get("ui.background"));
97 self.surface.set_style(area, theme.get("ui.background"));
@@ -221,8 +220,12 @@ impl Renderer {
222 // TODO: paint cursor heads except primary
221 // TODO: paint cursor heads except primary
224− self.surface
223+ self.surface.set_string(
225− .set_string(OFFSET + visual_x, line, grapheme, style);
224+ viewport.x + visual_x,
227 visual_x += width;
230 visual_x += width;
@@ -321,7 +324,7 @@ impl Renderer {
321 .set_string(2, self.size.1 - 1, &prompt.line, self.text_color);
324 .set_string(2, self.size.1 - 1, &prompt.line, self.text_color);
324− pub fn draw(&mut self) {
327+ pub fn draw_and_swap(&mut self) {
325 use tui::backend::Backend;
328 use tui::backend::Backend;
326 // TODO: theres probably a better place for this
329 // TODO: theres probably a better place for this
327 self.terminal
330 self.terminal
@@ -363,112 +366,40 @@ impl Renderer {
366−impl<'a> Application<'a> {
369+struct EditorView {
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+ editor: Editor,
370−
371+ prompt: Option<Prompt>, // TODO: this is None for now, make a layer
371− if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
372+ keymap: Keymaps,
372− editor.open(file, terminal.size)?;
375− let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
376+ fn new(editor: Editor) -> Self {
377− let mut app = Self {
377+ Self {
380− // TODO; move to state
381 prompt: None,
379 prompt: None,
384 keymap: keymap::default(),
380 keymap: keymap::default(),
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
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 {
404− self.terminal.render_prompt(view, prompt, theme_ref);
409− self.terminal.draw();
411− // TODO: drop unwrap
413− .render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
416− pub async fn event_loop(&mut self) {
417− let mut reader = EventStream::new();
420− self.language_server.initialize().await.unwrap();
421− self.language_server
422− .text_document_did_open(&self.editor.view().unwrap().doc)
429− if self.editor.should_close {
433− use futures_util::{select, FutureExt};
435− event = reader.next().fuse() => {
436− self.handle_terminal_events(event).await
438− call = self.language_server.incoming.next().fuse() => {
439− self.handle_language_server_message(call).await
445− pub async fn handle_terminal_events(
385+impl Component for EditorView {
446− &mut self,
386+ fn handle_event(&mut self, event: Event, executor: &smol::Executor) -> bool {
447− event: Option<Result<Event, crossterm::ErrorKind>>,
449− // Handle key events
450 match event {
387 match event {
451− Some(Ok(Event::Resize(width, height))) => {
388+ Event::Resize(width, height) => {
452− self.terminal.resize(width, height);
454 // TODO: simplistic ensure cursor in view for now
389 // TODO: simplistic ensure cursor in view for now
455 // TODO: loop over views
390 // TODO: loop over views
456 if let Some(view) = self.editor.view_mut() {
391 if let Some(view) = self.editor.view_mut() {
457− view.size = self.terminal.size;
392+ view.size = (width, height);
458 view.ensure_cursor_in_view()
393 view.ensure_cursor_in_view()
463− Some(Ok(Event::Key(event))) => {
396+ Event::Key(event) => {
464 // if there's a prompt, it takes priority
397 // if there's a prompt, it takes priority
465 if let Some(prompt) = &mut self.prompt {
398 if let Some(prompt) = &mut self.prompt {
466 self.prompt
399 self.prompt
467 .as_mut()
400 .as_mut()
468 .unwrap()
401 .unwrap()
469 .handle_input(event, &mut self.editor);
402 .handle_input(event, &mut self.editor);
472 } else if let Some(view) = self.editor.view_mut() {
403 } else if let Some(view) = self.editor.view_mut() {
473 let keys = vec![event];
404 let keys = vec![event];
474 // TODO: sequences (`gg`)
405 // TODO: sequences (`gg`)
@@ -478,7 +409,7 @@ impl<'a> Application<'a> {
478 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
409 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
479 let mut cx = helix_view::commands::Context {
410 let mut cx = helix_view::commands::Context {
481− executor: self.executor,
412+ executor: executor,
482 count: 1,
413 count: 1,
@@ -490,7 +421,7 @@ impl<'a> Application<'a> {
491 let mut cx = helix_view::commands::Context {
422 let mut cx = helix_view::commands::Context {
493− executor: self.executor,
424+ executor: executor,
494 count: 1,
425 count: 1,
496 commands::insert::insert_char(&mut cx, c);
427 commands::insert::insert_char(&mut cx, c);
@@ -557,7 +488,7 @@ impl<'a> Application<'a> {
557 } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
488 } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
558 let mut cx = helix_view::commands::Context {
489 let mut cx = helix_view::commands::Context {
560− executor: self.executor,
491+ executor: executor,
561 count: 1,
492 count: 1,
563 command(&mut cx);
494 command(&mut cx);
@@ -570,7 +501,7 @@ impl<'a> Application<'a> {
570 if let Some(command) = self.keymap[&mode].get(&keys) {
501 if let Some(command) = self.keymap[&mode].get(&keys) {
571 let mut cx = helix_view::commands::Context {
502 let mut cx = helix_view::commands::Context {
573− executor: self.executor,
504+ executor: executor,
574 count: 1,
505 count: 1,
576 command(&mut cx);
507 command(&mut cx);
@@ -580,10 +511,119 @@ impl<'a> Application<'a> {
586− Some(Ok(Event::Mouse(_))) => (), // unhandled
516+ Event::Mouse(_) => (),
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
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 {
534+ renderer.render_prompt(view, prompt, theme_ref);
539+ // TODO: drop unwrap
540+ renderer.render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
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();
549+ if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
550+ editor.open(file, renderer.size)?;
553+ let mut compositor = Compositor::new();
554+ compositor.push(Box::new(EditorView::new(editor)));
556+ let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
558+ let mut app = Self {
560+ // TODO; move to state
571+ fn render(&mut self) {
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();
578+ pub async fn event_loop(&mut self) {
579+ let mut reader = EventStream::new();
582+ self.language_server.initialize().await.unwrap();
584+ // self.language_server
585+ // .text_document_did_open(&self.editor.view().unwrap().doc)
593+ // if self.editor.should_close {
597+ use futures_util::{select, FutureExt};
599+ event = reader.next().fuse() => {
600+ self.handle_terminal_events(event)
602+ call = self.language_server.incoming.next().fuse() => {
603+ self.handle_language_server_message(call).await
609+ pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
610+ // Handle key events
612+ Some(Ok(Event::Resize(width, height))) => {
613+ self.renderer.resize(width, height);
615+ // TODO: use the response
617+ .handle_event(Event::Resize(width, height), self.executor);
621+ Some(Ok(event)) => {
622+ // TODO: use the response
623+ self.compositor.handle_event(event, self.executor);
587 Some(Err(x)) => panic!(x),
627 Some(Err(x)) => panic!(x),
588 None => panic!(),
628 None => panic!(),
@@ -599,11 +639,13 @@ impl<'a> Application<'a> {
599 match notification {
639 match notification {
600 Notification::PublishDiagnostics(params) => {
640 Notification::PublishDiagnostics(params) => {
601 let path = Some(params.uri.to_file_path().unwrap());
641 let path = Some(params.uri.to_file_path().unwrap());
602− let view = self
642+ let view: Option<&mut helix_view::View> = None;
603− .editor
645+ // .editor
604− .views
646+ // .views
605− .iter_mut()
647+ // .iter_mut()
606− .find(|view| view.doc.path == path);
648+ // .find(|view| view.doc.path == path);
608 if let Some(view) = view {
650 if let Some(view) = view {
609 let doc = view.doc.text().slice(..);
651 let doc = view.doc.text().slice(..);