From ef0d062b1fd202fe89bc4bbd33826c46f660ef70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Sun, 13 Dec 2020 13:29:34 +0900 Subject: [PATCH] Fix cursor positioning. --- helix-term/src/application.rs | 30 ++++-------------------------- helix-term/src/component.rs | 20 -------------------- helix-term/src/compositor.rs | 14 ++++++++++++++ helix-term/src/editor_view.rs | 24 ++++++++++++++++++++---- helix-term/src/prompt.rs | 8 ++++++++ 5 files changed, 46 insertions(+), 50 deletions(-) delete mode 100644 helix-term/src/component.rs diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 7a74f8ba..c25871c7 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -9,7 +9,6 @@ use crate::prompt::Prompt; use log::{debug, info}; use std::{ - borrow::Cow, io::{self, stdout, Stdout, Write}, path::PathBuf, time::Duration, @@ -47,31 +46,9 @@ pub struct Application { // TODO: temp #[inline(always)] pub fn text_color() -> Style { - return Style::default().fg(Color::Rgb(219, 191, 239)); // lilac + Style::default().fg(Color::Rgb(219, 191, 239)) // lilac } -// pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport: Rect) { -// let mut stdout = stdout(); -// match view.doc.mode() { -// Mode::Insert => write!(stdout, "\x1B[6 q"), -// mode => write!(stdout, "\x1B[2 q"), -// }; -// let pos = if let Some(prompt) = prompt { -// Position::new(self.size.0 as usize, 2 + prompt.cursor) -// } else { -// let cursor = view.doc.state.selection().cursor(); - -// let mut pos = view -// .screen_coords_at_pos(&view.doc.text().slice(..), cursor) -// .expect("Cursor is out of bounds."); -// pos.col += viewport.x as usize; -// pos.row += viewport.y as usize; -// pos -// }; - -// execute!(stdout, cursor::MoveTo(pos.col as u16, pos.row as u16)); -// } - impl Application { pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result { let backend = CrosstermBackend::new(stdout()); @@ -106,13 +83,14 @@ impl Application { let editor = &mut self.editor; let compositor = &self.compositor; - // TODO: should be unnecessary - // self.terminal.autoresize(); let mut cx = crate::compositor::Context { editor, executor }; let area = self.terminal.size().unwrap(); + compositor.render(area, self.terminal.current_buffer_mut(), &mut cx); + let pos = compositor.cursor_position(area, &mut cx); self.terminal.draw(); + self.terminal.set_cursor(pos.col as u16, pos.row as u16); } pub async fn event_loop(&mut self) { diff --git a/helix-term/src/component.rs b/helix-term/src/component.rs deleted file mode 100644 index 08d6c620..00000000 --- a/helix-term/src/component.rs +++ /dev/null @@ -1,20 +0,0 @@ -// IDEA: render to a cache buffer, then if not changed, copy the buf into the parent -type Surface = (); -pub trait Component { - /// Process input events, return true if handled. - fn process_event(&mut self, event: crossterm::event::Event, args: ()) -> bool; - /// Should redraw? Useful for saving redraw cycles if we know component didn't change. - fn should_update(&self) -> bool { - true - } - - fn render(&mut self, surface: &mut Surface, args: ()); -} - -// HStack / VStack -// focus by component id: each View/Editor gets it's own incremental id at create -// Component: View(Arc) -> multiple views can point to same state -// id 0 = prompt? -// when entering to prompt, it needs to direct Commands to last focus window -// -> prompt.trigger(focus_id), on_leave -> focus(focus_id) -// popups on another layer diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 1d94ee63..2e65f02a 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -14,6 +14,7 @@ // cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), ) use crossterm::event::Event; +use helix_core::Position; use smol::Executor; use tui::buffer::Buffer as Surface; use tui::layout::Rect; @@ -52,6 +53,10 @@ pub trait Component { } fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context); + + fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option { + None + } } // struct Editor { }; @@ -138,4 +143,13 @@ impl Compositor { layer.render(area, surface, cx) } } + + pub fn cursor_position(&self, area: Rect, cx: &mut Context) -> Position { + for layer in self.layers.iter().rev() { + if let Some(pos) = layer.cursor_position(area, cx) { + return pos; + } + } + panic!("No layer returned a position!"); + } } diff --git a/helix-term/src/editor_view.rs b/helix-term/src/editor_view.rs index 0181623a..b778e79b 100644 --- a/helix-term/src/editor_view.rs +++ b/helix-term/src/editor_view.rs @@ -21,6 +21,8 @@ pub struct EditorView { keymap: Keymaps, } +const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter + impl EditorView { pub fn new() -> Self { Self { @@ -34,11 +36,10 @@ impl EditorView { surface: &mut Surface, theme: &Theme, ) { - const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter let area = Rect::new(OFFSET, 0, viewport.width - OFFSET, viewport.height - 2); // - 2 for statusline and prompt self.render_buffer(view, area, surface, theme); let area = Rect::new(0, viewport.height - 2, viewport.width, 1); - self.render_statusline(view, viewport, surface, theme); + self.render_statusline(view, area, surface, theme); } // TODO: ideally not &mut View but highlights require it because of cursor cache @@ -218,7 +219,7 @@ impl EditorView { }; // statusline surface.set_style( - Rect::new(0, viewport.y, viewport.height, 1), + Rect::new(0, viewport.y, viewport.width, 1), theme.get("ui.statusline"), ); surface.set_string(1, viewport.y, mode, text_color()); @@ -306,6 +307,21 @@ impl Component for EditorView { } // TODO: drop unwrap - // TODO: !!! self.render_cursor(cx.editor.view().unwrap(), None, viewport); + } + + fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option { + // match view.doc.mode() { + // Mode::Insert => write!(stdout, "\x1B[6 q"), + // mode => write!(stdout, "\x1B[2 q"), + // }; + let view = ctx.editor.view().unwrap(); + let cursor = view.doc.state.selection().cursor(); + + let mut pos = view + .screen_coords_at_pos(&view.doc.text().slice(..), cursor) + .expect("Cursor is out of bounds."); + pos.col += area.x as usize + OFFSET as usize; + pos.row += area.y as usize; + Some(pos) } } diff --git a/helix-term/src/prompt.rs b/helix-term/src/prompt.rs index 4747c9f5..7f473ebc 100644 --- a/helix-term/src/prompt.rs +++ b/helix-term/src/prompt.rs @@ -1,5 +1,6 @@ use crate::compositor::{Component, Context, EventResult}; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; +use helix_core::Position; use helix_view::Editor; use helix_view::Theme; use std::string::String; @@ -200,4 +201,11 @@ impl Component for Prompt { fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) { self.render_prompt(area, surface, &cx.editor.theme) } + + fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option { + Some(Position::new( + area.height as usize - 1, + area.x as usize + 2 + self.cursor, + )) + } }