on master help sign in
Fix cursor positioning.
ef0d062Blaž Hrastnik
helix-term/src/application.rs+4−26
@@ -9,7 +9,6 @@ use crate::prompt::Prompt;
9use log::{debug, info};
9use log::{debug, info};
10
10
11use std::{
11use std::{
12− borrow::Cow,
13 io::{self, stdout, Stdout, Write},
12 io::{self, stdout, Stdout, Write},
14 path::PathBuf,
13 path::PathBuf,
15 time::Duration,
14 time::Duration,
@@ -47,31 +46,9 @@ pub struct Application {
47// TODO: temp
46// TODO: temp
48#[inline(always)]
47#[inline(always)]
49pub fn text_color() -> Style {
48pub fn text_color() -> Style {
50− return Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
49+ Style::default().fg(Color::Rgb(219, 191, 239)) // lilac
51}
50}
52
51
53−// pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport: Rect) {
54−// let mut stdout = stdout();
55−// match view.doc.mode() {
56−// Mode::Insert => write!(stdout, "\x1B[6 q"),
57−// mode => write!(stdout, "\x1B[2 q"),
58−// };
59−// let pos = if let Some(prompt) = prompt {
60−// Position::new(self.size.0 as usize, 2 + prompt.cursor)
61−// } else {
62−// let cursor = view.doc.state.selection().cursor();
63−
64−// let mut pos = view
65−// .screen_coords_at_pos(&view.doc.text().slice(..), cursor)
66−// .expect("Cursor is out of bounds.");
67−// pos.col += viewport.x as usize;
68−// pos.row += viewport.y as usize;
69−// pos
70−// };
71−
72−// execute!(stdout, cursor::MoveTo(pos.col as u16, pos.row as u16));
73−// }
74−
75impl Application {
52impl Application {
76 pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result<Self, Error> {
53 pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result<Self, Error> {
77 let backend = CrosstermBackend::new(stdout());
54 let backend = CrosstermBackend::new(stdout());
@@ -106,13 +83,14 @@ impl Application {
106 let editor = &mut self.editor;
83 let editor = &mut self.editor;
107 let compositor = &self.compositor;
84 let compositor = &self.compositor;
108
85
109− // TODO: should be unnecessary
110− // self.terminal.autoresize();
111 let mut cx = crate::compositor::Context { editor, executor };
86 let mut cx = crate::compositor::Context { editor, executor };
112 let area = self.terminal.size().unwrap();
87 let area = self.terminal.size().unwrap();
88+
113 compositor.render(area, self.terminal.current_buffer_mut(), &mut cx);
89 compositor.render(area, self.terminal.current_buffer_mut(), &mut cx);
90+ let pos = compositor.cursor_position(area, &mut cx);
114
91
115 self.terminal.draw();
92 self.terminal.draw();
93+ self.terminal.set_cursor(pos.col as u16, pos.row as u16);
116 }
94 }
117
95
118 pub async fn event_loop(&mut self) {
96 pub async fn event_loop(&mut self) {
helix-term/src/component.rsdeleted−20
@@ -1,20 +0,0 @@
1−// IDEA: render to a cache buffer, then if not changed, copy the buf into the parent
2−type Surface = ();
3−pub trait Component {
4− /// Process input events, return true if handled.
5− fn process_event(&mut self, event: crossterm::event::Event, args: ()) -> bool;
6− /// Should redraw? Useful for saving redraw cycles if we know component didn't change.
7− fn should_update(&self) -> bool {
8− true
9− }
10−
11− fn render(&mut self, surface: &mut Surface, args: ());
12−}
13−
14−// HStack / VStack
15−// focus by component id: each View/Editor gets it's own incremental id at create
16−// Component: View(Arc<State>) -> multiple views can point to same state
17−// id 0 = prompt?
18−// when entering to prompt, it needs to direct Commands to last focus window
19−// -> prompt.trigger(focus_id), on_leave -> focus(focus_id)
20−// popups on another layer
helix-term/src/compositor.rs+14
@@ -14,6 +14,7 @@
14// cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
14// cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
15
15
16use crossterm::event::Event;
16use crossterm::event::Event;
17+use helix_core::Position;
17use smol::Executor;
18use smol::Executor;
18use tui::buffer::Buffer as Surface;
19use tui::buffer::Buffer as Surface;
19use tui::layout::Rect;
20use tui::layout::Rect;
@@ -52,6 +53,10 @@ pub trait Component {
52 }
53 }
53
54
54 fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context);
55 fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context);
56+
57+ fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
58+ None
59+ }
55}
60}
56
61
57// struct Editor { };
62// struct Editor { };
@@ -138,4 +143,13 @@ impl Compositor {
138 layer.render(area, surface, cx)
143 layer.render(area, surface, cx)
139 }
144 }
140 }
145 }
146+
147+ pub fn cursor_position(&self, area: Rect, cx: &mut Context) -> Position {
148+ for layer in self.layers.iter().rev() {
149+ if let Some(pos) = layer.cursor_position(area, cx) {
150+ return pos;
151+ }
152+ }
153+ panic!("No layer returned a position!");
154+ }
141}
155}
helix-term/src/editor_view.rs+20−4
@@ -21,6 +21,8 @@ pub struct EditorView {
21 keymap: Keymaps,
21 keymap: Keymaps,
22}
22}
23
23
24+const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
25+
24impl EditorView {
26impl EditorView {
25 pub fn new() -> Self {
27 pub fn new() -> Self {
26 Self {
28 Self {
@@ -34,11 +36,10 @@ impl EditorView {
34 surface: &mut Surface,
36 surface: &mut Surface,
35 theme: &Theme,
37 theme: &Theme,
36 ) {
38 ) {
37− const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
38 let area = Rect::new(OFFSET, 0, viewport.width - OFFSET, viewport.height - 2); // - 2 for statusline and prompt
39 let area = Rect::new(OFFSET, 0, viewport.width - OFFSET, viewport.height - 2); // - 2 for statusline and prompt
39 self.render_buffer(view, area, surface, theme);
40 self.render_buffer(view, area, surface, theme);
40 let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
41 let area = Rect::new(0, viewport.height - 2, viewport.width, 1);
41− self.render_statusline(view, viewport, surface, theme);
42+ self.render_statusline(view, area, surface, theme);
42 }
43 }
43
44
44 // TODO: ideally not &mut View but highlights require it because of cursor cache
45 // TODO: ideally not &mut View but highlights require it because of cursor cache
@@ -218,7 +219,7 @@ impl EditorView {
218 };
219 };
219 // statusline
220 // statusline
220 surface.set_style(
221 surface.set_style(
221− Rect::new(0, viewport.y, viewport.height, 1),
222+ Rect::new(0, viewport.y, viewport.width, 1),
222 theme.get("ui.statusline"),
223 theme.get("ui.statusline"),
223 );
224 );
224 surface.set_string(1, viewport.y, mode, text_color());
225 surface.set_string(1, viewport.y, mode, text_color());
@@ -306,6 +307,21 @@ impl Component for EditorView {
306 }
307 }
307
308
308 // TODO: drop unwrap
309 // TODO: drop unwrap
310+ }
311+
312+ fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
313+ // match view.doc.mode() {
314+ // Mode::Insert => write!(stdout, "\x1B[6 q"),
315+ // mode => write!(stdout, "\x1B[2 q"),
316+ // };
309− // TODO: !!! self.render_cursor(cx.editor.view().unwrap(), None, viewport);
317+ let view = ctx.editor.view().unwrap();
318+ let cursor = view.doc.state.selection().cursor();
319+
320+ let mut pos = view
321+ .screen_coords_at_pos(&view.doc.text().slice(..), cursor)
322+ .expect("Cursor is out of bounds.");
323+ pos.col += area.x as usize + OFFSET as usize;
324+ pos.row += area.y as usize;
325+ Some(pos)
310 }
326 }
311}
327}
helix-term/src/prompt.rs+8
@@ -1,5 +1,6 @@
1use crate::compositor::{Component, Context, EventResult};
1use crate::compositor::{Component, Context, EventResult};
2use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
2use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
3+use helix_core::Position;
3use helix_view::Editor;
4use helix_view::Editor;
4use helix_view::Theme;
5use helix_view::Theme;
5use std::string::String;
6use std::string::String;
@@ -200,4 +201,11 @@ impl Component for Prompt {
200 fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
201 fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
201 self.render_prompt(area, surface, &cx.editor.theme)
202 self.render_prompt(area, surface, &cx.editor.theme)
202 }
203 }
204+
205+ fn cursor_position(&self, area: Rect, ctx: &mut Context) -> Option<Position> {
206+ Some(Position::new(
207+ area.height as usize - 1,
208+ area.x as usize + 2 + self.cursor,
209+ ))
210+ }
203}
211}