on master help sign in
Refactor Editor into Application and Editor/Workspace.
commit49cc6c19244462b80beeac96be0ea0cc5bc1febc authorBlaž Hrastnik <blaz@mxxn.io> dateFri 16 Oct 2020, 12:29 (6 years ago) parent2676023
4 files changed+133−91
helix-term/src/editor.rs → helix-term/src/application.rs+108−88 helix-term/src/main.rs+3−3 helix-view/src/editor.rs+20 helix-view/src/lib.rs+2
helix-term/src/editor.rs → helix-term/src/application.rsrenamed+108−88
@@ -1,6 +1,6 @@
11use clap::ArgMatches as Args;
22use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State};
3−use helix_view::{commands, keymap, prompt::Prompt, View};
3+use helix_view::{commands, keymap, prompt::Prompt, Editor, View};
44
55use std::{
66 borrow::Cow,
@@ -18,11 +18,15 @@ use crossterm::{
1818 cursor::position,
1919 event::{self, read, Event, EventStream, KeyCode, KeyEvent},
2020 execute, queue,
21− style::{Color, Print, SetForegroundColor},
2221 terminal::{self, disable_raw_mode, enable_raw_mode},
2322};
2423
25−use tui::{backend::CrosstermBackend, buffer::Buffer as Surface, layout::Rect, style::Style};
24+use tui::{
25+ backend::CrosstermBackend,
26+ buffer::Buffer as Surface,
27+ layout::Rect,
28+ style::{Color, Style},
29+};
2630
2731const OFFSET: u16 = 6; // 5 linenr + 1 gutter
2832
@@ -30,78 +34,54 @@ type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
3034
3135static EX: smol::Executor = smol::Executor::new();
3236
33−pub struct Editor {
34− terminal: Terminal,
35− view: Option<View>,
37+pub struct Application {
38+ editor: Editor,
39+ prompt: Option<Prompt>,
40+ should_close: bool,
41+ terminal: Renderer,
42+}
43+
44+struct Renderer {
3645 size: (u16, u16),
46+ terminal: Terminal,
3747 surface: Surface,
3848 cache: Surface,
39− prompt: Option<Prompt>,
40− should_close: bool,
49+ text_color: Style,
4150}
4251
43−impl Editor {
44− pub fn new(mut args: Args) -> Result<Self, Error> {
52+impl Renderer {
53+ pub fn new() -> Result<Self, Error> {
4554 let backend = CrosstermBackend::new(stdout());
46−
4755 let mut terminal = Terminal::new(backend)?;
4856 let size = terminal::size().unwrap();
57+ let text_color: Style = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
58+
4959 let area = Rect::new(0, 0, size.0, size.1);
5060
51− let mut editor = Editor {
52− terminal,
53− view: None,
61+ Ok(Self {
5462 size,
63+ terminal,
5564 surface: Surface::empty(area),
5665 cache: Surface::empty(area),
57− // TODO; move to state
58− prompt: None,
59− should_close: false,
60− };
61−
62− if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
63− editor.open(file)?;
64− }
65−
66− Ok(editor)
67− }
68−
69− pub fn set_prompt(self) {
70− let commands = |input: &str| match input {
71− "q" => self.should_close = true,
72− _ => (),
73− };
74− let prompt = Prompt::new(|input| None, commands);
75− self.prompt = Some(prompt);
66+ text_color,
67+ })
7668 }
7769
78− pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
79− self.view = Some(View::open(path, self.size)?);
80− Ok(())
81− }
82−
83− fn render(&mut self) {
84− use tui::style::Color;
85− // TODO: ideally not mut but highlights require it because of cursor cache
86− let viewport = Rect::new(OFFSET, 0, self.size.0, self.size.1 - 2); // - 2 for statusline and prompt
87− let text_color: Style = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
88−
89− self.render_view(viewport, text_color);
90−
91− self.render_prompt(text_color);
92−
93− self.render_cursor(viewport, text_color);
70+ pub fn resize(&mut self, width: u16, height: u16) {
71+ self.size = (width, height);
72+ let area = Rect::new(0, 0, width, height);
73+ self.surface = Surface::empty(area);
74+ self.cache = Surface::empty(area);
9475 }
9576
96− pub fn render_view(&mut self, viewport: Rect, text_color: Style) {
97− self.render_buffer(viewport);
98− self.render_statusline(text_color);
77+ pub fn render_view(&mut self, view: &mut View, viewport: Rect) {
78+ self.render_buffer(view, viewport);
79+ self.render_statusline(view);
9980 }
10081
101− pub fn render_buffer(&mut self, viewport: Rect) {
102− use tui::style::Color;
82+ // TODO: ideally not &mut View but highlights require it because of cursor cache
83+ pub fn render_buffer(&mut self, view: &mut View, viewport: Rect) {
10384 let area = Rect::new(0, 0, self.size.0, self.size.1);
104− let mut view: &mut View = self.view.as_mut().unwrap();
10585 self.surface.reset(); // reset is faster than allocating new empty surface
10686
10787 // clear with background color
@@ -241,8 +221,7 @@ impl Editor {
241221 }
242222 }
243223
244− pub fn render_statusline(&mut self, text_color: Style) {
245− let view = self.view.as_ref().unwrap();
224+ pub fn render_statusline(&mut self, view: &View) {
246225 let mode = match view.state.mode() {
247226 Mode::Insert => "INS",
248227 Mode::Normal => "NOR",
@@ -255,24 +234,16 @@ impl Editor {
255234 view.theme.get("ui.statusline"),
256235 );
257236 self.surface
258− .set_string(1, self.size.1 - 2, mode, text_color);
237+ .set_string(1, self.size.1 - 2, mode, self.text_color);
259238 }
260239
261− pub fn render_prompt(&mut self, text_color: Style) {
262− // TODO: maybe name this render_commandline
240+ pub fn render_prompt(&mut self, prompt: &Prompt) {
263241 use tui::backend::Backend;
264− let view = self.view.as_ref().unwrap();
265242 // render buffer text
266− let buffer_string;
267− if view.state.mode == Mode::Command {
268− buffer_string = &self.prompt.unwrap().buffer;
269− self.surface
270− .set_string(1, self.size.1 - 1, String::from(":"), text_color);
271− self.surface
272− .set_string(2, self.size.1 - 1, buffer_string, text_color);
273− } else {
274− buffer_string = &String::from("");
275− }
243+ self.surface
244+ .set_string(1, self.size.1 - 1, String::from(":"), self.text_color);
245+ self.surface
246+ .set_string(2, self.size.1 - 1, &prompt.buffer, self.text_color);
276247
277248 // TODO: theres probably a better place for this
278249 self.terminal
@@ -282,33 +253,85 @@ impl Editor {
282253 std::mem::swap(&mut self.surface, &mut self.cache);
283254 }
284255
285− pub fn render_cursor(&mut self, viewport: Rect, text_color: Style) {
286− let mut pos: Position;
287− let view = self.view.as_ref().unwrap();
256+ pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport: Rect) {
288257 let mut stdout = stdout();
289258 match view.state.mode() {
290259 Mode::Insert => write!(stdout, "\x1B[6 q"),
291260 mode => write!(stdout, "\x1B[2 q"),
292261 };
293− if view.state.mode() == Mode::Command {
294− pos = Position::new(self.size.0 as usize, 2 + self.prompt.unwrap().cursor_loc);
262+ let pos = if let Some(prompt) = prompt {
263+ Position::new(self.size.0 as usize, 2 + prompt.cursor_loc)
295264 } else {
296265 if let Some(path) = view.state.path() {
297− self.surface
298− .set_string(6, self.size.1 - 1, path.to_string_lossy(), text_color);
266+ self.surface.set_string(
267+ 6,
268+ self.size.1 - 1,
269+ path.to_string_lossy(),
270+ self.text_color,
271+ );
299272 }
300273
301274 let cursor = view.state.selection().cursor();
302275
303− pos = view
276+ let mut pos = view
304277 .screen_coords_at_pos(&view.state.doc().slice(..), cursor)
305278 .expect("Cursor is out of bounds.");
306279 pos.col += viewport.x as usize;
307280 pos.row += viewport.y as usize;
308− }
281+ pos
282+ };
309283
310284 execute!(stdout, cursor::MoveTo(pos.col as u16, pos.row as u16));
311285 }
286+}
287+
288+impl Application {
289+ pub fn new(mut args: Args) -> Result<Self, Error> {
290+ let terminal = Renderer::new()?;
291+ let mut editor = Editor::new();
292+
293+ if let Some(file) = args.values_of_t::<PathBuf>("files").unwrap().pop() {
294+ editor.open(file, terminal.size)?;
295+ }
296+
297+ let mut app = Self {
298+ editor,
299+ terminal,
300+ // TODO; move to state
301+ prompt: None,
302+ should_close: false,
303+ };
304+
305+ Ok(app)
306+ }
307+
308+ pub fn set_prompt(&mut self) {
309+ // let commands = |input| match input {
310+ // "q" => self.should_close = true,
311+ // _ => (),
312+ // };
313+ // let prompt = Prompt::new(|input| None, commands);
314+ // self.prompt = Some(prompt);
315+ }
316+
317+ fn render(&mut self) {
318+ let viewport = Rect::new(OFFSET, 0, self.terminal.size.0, self.terminal.size.1 - 2); // - 2 for statusline and prompt
319+
320+ if let Some(view) = &mut self.editor.view {
321+ self.terminal.render_view(view, viewport);
322+ }
323+
324+ if let Some(prompt) = &self.prompt {
325+ self.terminal.render_prompt(prompt);
326+ }
327+
328+ // TODO: drop unwrap
329+ self.terminal.render_cursor(
330+ self.editor.view.as_ref().unwrap(),
331+ self.prompt.as_ref(),
332+ viewport,
333+ );
334+ }
312335
313336 pub async fn event_loop(&mut self) {
314337 let mut reader = EventStream::new();
@@ -325,14 +348,11 @@ impl Editor {
325348 let mut event = reader.next().await;
326349 match event {
327350 Some(Ok(Event::Resize(width, height))) => {
328− self.size = (width, height);
329− let area = Rect::new(0, 0, width, height);
330− self.surface = Surface::empty(area);
331− self.cache = Surface::empty(area);
351+ self.terminal.resize(width, height);
332352
333353 // TODO: simplistic ensure cursor in view for now
334− if let Some(view) = &mut self.view {
335− view.size = self.size;
354+ if let Some(view) = &mut self.editor.view {
355+ view.size = self.terminal.size;
336356 view.ensure_cursor_in_view()
337357 };
338358
@@ -341,7 +361,7 @@ impl Editor {
341361 Some(Ok(Event::Key(event))) => {
342362 // TODO: sequences (`gg`)
343363 // TODO: handle count other than 1
344− if let Some(view) = &mut self.view {
364+ if let Some(view) = &mut self.editor.view {
345365 let keys = vec![event];
346366 match view.state.mode() {
347367 Mode::Insert => {
@@ -357,7 +377,7 @@ impl Editor {
357377 view.ensure_cursor_in_view();
358378 }
359379 Mode::Command => {
360− self.prompt.unwrap().handle_input(event, view);
380+ self.prompt.as_mut().unwrap().handle_input(event, view);
361381 }
362382 mode => {
363383 if let Some(command) = keymap[&mode].get(&keys) {
helix-term/src/main.rs+3−3
@@ -1,8 +1,8 @@
11#![allow(unused)]
22
3−mod editor;
3+mod application;
44
5−use editor::Editor;
5+use application::Application;
66
77use clap::{App, Arg};
88use std::path::PathBuf;
@@ -27,7 +27,7 @@ fn main() -> Result<(), Error> {
2727 }
2828
2929 smol::block_on(EX.run(async {
30− editor::Editor::new(args).unwrap().run().await;
30+ Application::new(args).unwrap().run().await;
3131 }));
3232
3333 Ok(())
helix-view/src/editor.rsnew file+20
@@ -0,0 +1,20 @@
1+use crate::View;
2+
3+use std::path::PathBuf;
4+
5+use anyhow::Error;
6+
7+pub struct Editor {
8+ pub view: Option<View>,
9+}
10+
11+impl Editor {
12+ pub fn new() -> Self {
13+ Self { view: None }
14+ }
15+
16+ pub fn open(&mut self, path: PathBuf, size: (u16, u16)) -> Result<(), Error> {
17+ self.view = Some(View::open(path, size)?);
18+ Ok(())
19+ }
20+}
helix-view/src/lib.rs+2
@@ -1,7 +1,9 @@
11pub mod commands;
2+pub mod editor;
23pub mod keymap;
34pub mod prompt;
45pub mod theme;
56pub mod view;
67
8+pub use editor::Editor;
79pub use view::View;