mirror of https://github.com/helix-editor/helix
Fix cursor positioning.
parent
8695415fbf
commit
ef0d062b1f
|
@ -9,7 +9,6 @@ use crate::prompt::Prompt;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
|
||||||
io::{self, stdout, Stdout, Write},
|
io::{self, stdout, Stdout, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
@ -47,31 +46,9 @@ pub struct Application {
|
||||||
// TODO: temp
|
// TODO: temp
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn text_color() -> Style {
|
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 {
|
impl Application {
|
||||||
pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result<Self, Error> {
|
pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result<Self, Error> {
|
||||||
let backend = CrosstermBackend::new(stdout());
|
let backend = CrosstermBackend::new(stdout());
|
||||||
|
@ -106,13 +83,14 @@ impl Application {
|
||||||
let editor = &mut self.editor;
|
let editor = &mut self.editor;
|
||||||
let compositor = &self.compositor;
|
let compositor = &self.compositor;
|
||||||
|
|
||||||
// TODO: should be unnecessary
|
|
||||||
// self.terminal.autoresize();
|
|
||||||
let mut cx = crate::compositor::Context { editor, executor };
|
let mut cx = crate::compositor::Context { editor, executor };
|
||||||
let area = self.terminal.size().unwrap();
|
let area = self.terminal.size().unwrap();
|
||||||
|
|
||||||
compositor.render(area, self.terminal.current_buffer_mut(), &mut cx);
|
compositor.render(area, self.terminal.current_buffer_mut(), &mut cx);
|
||||||
|
let pos = compositor.cursor_position(area, &mut cx);
|
||||||
|
|
||||||
self.terminal.draw();
|
self.terminal.draw();
|
||||||
|
self.terminal.set_cursor(pos.col as u16, pos.row as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn event_loop(&mut self) {
|
pub async fn event_loop(&mut self) {
|
||||||
|
|
|
@ -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<State>) -> 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
|
|
|
@ -14,6 +14,7 @@
|
||||||
// cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
|
// cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
|
||||||
|
|
||||||
use crossterm::event::Event;
|
use crossterm::event::Event;
|
||||||
|
use helix_core::Position;
|
||||||
use smol::Executor;
|
use smol::Executor;
|
||||||
use tui::buffer::Buffer as Surface;
|
use tui::buffer::Buffer as Surface;
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
@ -52,6 +53,10 @@ pub trait Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context);
|
fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context);
|
||||||
|
|
||||||
|
fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct Editor { };
|
// struct Editor { };
|
||||||
|
@ -138,4 +143,13 @@ impl Compositor {
|
||||||
layer.render(area, surface, cx)
|
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!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub struct EditorView {
|
||||||
keymap: Keymaps,
|
keymap: Keymaps,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||||
|
|
||||||
impl EditorView {
|
impl EditorView {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -34,11 +36,10 @@ impl EditorView {
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
theme: &Theme,
|
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
|
let area = Rect::new(OFFSET, 0, viewport.width - OFFSET, viewport.height - 2); // - 2 for statusline and prompt
|
||||||
self.render_buffer(view, area, surface, theme);
|
self.render_buffer(view, area, surface, theme);
|
||||||
let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
|
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
|
// TODO: ideally not &mut View but highlights require it because of cursor cache
|
||||||
|
@ -218,7 +219,7 @@ impl EditorView {
|
||||||
};
|
};
|
||||||
// statusline
|
// statusline
|
||||||
surface.set_style(
|
surface.set_style(
|
||||||
Rect::new(0, viewport.y, viewport.height, 1),
|
Rect::new(0, viewport.y, viewport.width, 1),
|
||||||
theme.get("ui.statusline"),
|
theme.get("ui.statusline"),
|
||||||
);
|
);
|
||||||
surface.set_string(1, viewport.y, mode, text_color());
|
surface.set_string(1, viewport.y, mode, text_color());
|
||||||
|
@ -306,6 +307,21 @@ impl Component for EditorView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: drop unwrap
|
// TODO: drop unwrap
|
||||||
// TODO: !!! self.render_cursor(cx.editor.view().unwrap(), None, viewport);
|
}
|
||||||
|
|
||||||
|
fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::compositor::{Component, Context, EventResult};
|
use crate::compositor::{Component, Context, EventResult};
|
||||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
||||||
|
use helix_core::Position;
|
||||||
use helix_view::Editor;
|
use helix_view::Editor;
|
||||||
use helix_view::Theme;
|
use helix_view::Theme;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
@ -200,4 +201,11 @@ impl Component for Prompt {
|
||||||
fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||||
self.render_prompt(area, surface, &cx.editor.theme)
|
self.render_prompt(area, surface, &cx.editor.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
|
||||||
|
Some(Position::new(
|
||||||
|
area.height as usize - 1,
|
||||||
|
area.x as usize + 2 + self.cursor,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue