From 19643446cd2d5911b56af6f52f0f40da39a14109 Mon Sep 17 00:00:00 2001 From: Jan Hrastnik Date: Thu, 4 Jun 2020 01:05:01 +0200 Subject: editor.rs now uses crossterm --- helix-term/src/editor.rs | 318 ++++++++--------------------------------------- helix-term/src/main.rs | 6 +- 2 files changed, 53 insertions(+), 271 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index c4aa72e6..1ea37053 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -1,287 +1,69 @@ -use anyhow::Error; -use termwiz::caps::Capabilities; -use termwiz::cell::AttributeChange; -use termwiz::color::{AnsiColor, ColorAttribute, RgbColor}; -use termwiz::input::*; -use termwiz::surface::Change; -use termwiz::terminal::{buffered::BufferedTerminal, SystemTerminal, Terminal}; -use termwiz::widgets::*; +use std::io::{self, stdout, Write}; -use crate::Args; +use crossterm::{ + cursor, + cursor::position, + event::{self, read, Event, KeyCode, KeyEvent}, + execute, style, + terminal::{self, disable_raw_mode, enable_raw_mode}, + Result, +}; -use std::{env, path::PathBuf}; +const HELP: &str = r#" + - Use q to quit + - Move cursor with h, j, k, l +"#; -use helix_core::Buffer; +pub struct Editor {} -/// This is a widget for our application -pub struct MainScreen {} - -impl MainScreen { - pub fn new() -> Self { - Self {} - } -} - -impl Widget for MainScreen { - fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool { - true // handled it all - } - - /// Draw ourselves into the surface provided by RenderArgs - fn render(&mut self, args: &mut RenderArgs) { - // args.surface.add_change(Change::ClearScreen( - // ColorAttribute::TrueColorWithPaletteFallback( - // RgbColor::new(0x31, 0x1B, 0x92), - // AnsiColor::Black.into(), - // ), - // )); - // args.surface - // .add_change(Change::Attribute(AttributeChange::Foreground( - // ColorAttribute::TrueColorWithPaletteFallback( - // RgbColor::new(0xB3, 0x88, 0xFF), - // AnsiColor::Purple.into(), - // ), - // ))); - } - - fn get_size_constraints(&self) -> layout::Constraints { - let mut constraints = layout::Constraints::default(); - constraints.child_orientation = layout::ChildOrientation::Vertical; - constraints - } -} - -pub struct BufferComponent<'a> { - text: String, - buffer: &'a mut Buffer, - - first_line: usize, -} - -impl<'a> BufferComponent<'a> { - /// Initialize the widget with the input text - pub fn new(buffer: &'a mut Buffer) -> Self { - Self { - buffer, - text: String::new(), - - first_line: 0, - } - } -} - -impl<'a> Widget for BufferComponent<'a> { - fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool { - match event { - WidgetEvent::Input(InputEvent::Key(KeyEvent { - key: KeyCode::Char('k'), - .. - })) => { - self.first_line = self.first_line.saturating_sub(1); - } - WidgetEvent::Input(InputEvent::Key(KeyEvent { - key: KeyCode::Char('j'), - .. - })) => { - self.first_line = self.first_line.saturating_add(1); - } - WidgetEvent::Input(InputEvent::Key(KeyEvent { - key: KeyCode::Enter, +impl Editor { + pub fn read_char() -> Result { + loop { + if let Ok(Event::Key(KeyEvent { + code: KeyCode::Char(c), .. - })) => { - self.text.push_str("\r\n"); + })) = event::read() + { + return Ok(c); } - WidgetEvent::Input(InputEvent::Paste(s)) => { - self.text.push_str(&s); - } - _ => {} } - - true // handled it all } - /// Draw ourselves into the surface provided by RenderArgs - fn render(&mut self, args: &mut RenderArgs) { - args.surface - .add_change(Change::ClearScreen(ColorAttribute::Default)); - - // args.surface - // .add_change(Change::Attribute(AttributeChange::Foreground( - // ColorAttribute::TrueColorWithPaletteFallback( - // RgbColor::new(0x11, 0x00, 0xFF), - // AnsiColor::Purple.into(), - // ), - // ))); - let (_width, height) = args.surface.dimensions(); - - for line in self.buffer.contents.lines_at(self.first_line).take(height) { - args.surface - .add_change(unsafe { String::from_utf8_unchecked(line.bytes().collect()) }); - args.surface.add_change("\r"); - } - // args.surface - // .add_change(format!("🤷 surface size is {:?}\r\n", dims)); - // args.surface.add_change(self.text.clone()); - - // Place the cursor at the end of the text. - // A more advanced text editing widget would manage the - // cursor position differently. - *args.cursor = CursorShapeAndPosition { - coords: args.surface.cursor_position().into(), - shape: termwiz::surface::CursorShape::SteadyBar, - ..Default::default() - }; - } - - fn get_size_constraints(&self) -> layout::Constraints { - let mut c = layout::Constraints::default(); - c.set_valign(layout::VerticalAlignment::Top); - c - } -} - -pub struct StatusLine {} - -impl StatusLine { - pub fn new() -> Self { - StatusLine {} - } -} -impl Widget for StatusLine { - fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool { - true - } - - fn render(&mut self, args: &mut RenderArgs) { - args.surface.add_change(Change::ClearScreen( - ColorAttribute::TrueColorWithPaletteFallback( - RgbColor::new(0xFF, 0xFF, 0xFF), - AnsiColor::Black.into(), - ), - )); - args.surface - .add_change(Change::Attribute(AttributeChange::Foreground( - ColorAttribute::TrueColorWithPaletteFallback( - RgbColor::new(0x00, 0x00, 0x00), - AnsiColor::Black.into(), - ), - ))); - - args.surface.add_change(" helix"); - } - - fn get_size_constraints(&self) -> layout::Constraints { - *layout::Constraints::default() - .set_fixed_height(1) - .set_valign(layout::VerticalAlignment::Bottom) - } -} - -pub struct Editor { - terminal: BufferedTerminal, - - buffer: Option, -} - -impl Editor { - pub fn new(mut args: Args) -> Result { - // Create a terminal - let caps = Capabilities::new_from_env()?; - let mut terminal = BufferedTerminal::new(SystemTerminal::new(caps)?)?; - - let mut editor = Editor { - terminal, - buffer: None, - }; - - if let Some(file) = args.files.pop() { - editor.open(file)?; + pub fn print_events() -> Result<()> { + loop { + // Handle key events + match Editor::read_char()? { + 'h' => execute!(io::stdout(), cursor::MoveLeft(1))?, + 'j' => execute!(io::stdout(), cursor::MoveDown(1))?, + 'k' => execute!(io::stdout(), cursor::MoveUp(1))?, + 'l' => execute!(io::stdout(), cursor::MoveRight(1))?, + 'q' => { + execute!( + io::stdout(), + style::ResetColor, + cursor::Show, + terminal::LeaveAlternateScreen + )?; + break; + } + _ => println!("use 'q' to quit."), + } } - Ok(editor) - } - - pub fn open(&mut self, path: PathBuf) -> Result<(), Error> { - let buffer = Buffer::load(path)?; - self.buffer = Some(buffer); Ok(()) } - pub fn run(&mut self) -> Result<(), Error> { - // Start with an empty string; typing into the app will - // update this string. - let mut typed_text = String::new(); - - { - let buf = &mut self.terminal; - // Put the terminal in raw mode + alternate screen - buf.terminal().enter_alternate_screen()?; - buf.terminal().set_raw_mode()?; - - // Set up the UI - let mut ui = Ui::new(); - - let root_id = ui.set_root(MainScreen::new()); - let buffer_id = - ui.add_child(root_id, BufferComponent::new(self.buffer.as_mut().unwrap())); - // let root_id = ui.set_root(Buffer::new(&mut typed_text)); - ui.add_child(root_id, StatusLine::new()); - ui.set_focus(buffer_id); + pub fn run() -> Result<()> { + enable_raw_mode()?; - loop { - ui.process_event_queue()?; - - // After updating and processing all of the widgets, compose them - // and render them to the screen. - if ui.render_to_screen(buf)? { - // We have more events to process immediately; don't block waiting - // for input below, but jump to the top of the loop to re-run the - // updates. - continue; - } - // Compute an optimized delta to apply to the terminal and display it - buf.flush()?; - - // Wait for user input - match buf.terminal().poll_input(None) { - Ok(Some(InputEvent::Resized { rows, cols })) => { - // FIXME: this is working around a bug where we don't realize - // that we should redraw everything on resize in BufferedTerminal. - buf.add_change(Change::ClearScreen(Default::default())); - buf.resize(cols, rows); - } - Ok(Some(input)) => match input { - InputEvent::Key(KeyEvent { - key: KeyCode::Escape, - .. - }) => { - // Quit the app when escape is pressed - break; - } - input @ _ => { - // Feed input into the Ui - ui.queue_event(WidgetEvent::Input(input)); - } - }, - Ok(None) => {} - Err(e) => { - print!("{:?}\r\n", e); - break; - } - } - } + // used for clearing the screen + execute!(io::stdout(), terminal::EnterAlternateScreen)?; + println!("{}", HELP); + let mut stdout = stdout(); + if let Err(e) = Editor::print_events() { + println!("Error: {:?}\r", e); } - // After we've stopped the full screen raw terminal, - // print out the final edited value of the input text. - println!("The text you entered: {}", typed_text); - Ok(()) - } - - pub fn render(&self) { - // create a new window sized surface - // paint all components - // diff vs last frame, swap - // paint diff + disable_raw_mode() } } diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index 6a04bdb7..11c700e7 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -1,9 +1,10 @@ #![allow(unused)] // mod editor; // mod component; +mod editor; mod keymap; -// use editor::Editor; +use editor::Editor; use argh::FromArgs; use std::path::PathBuf; @@ -19,9 +20,8 @@ pub struct Args { fn main() -> Result<(), Error> { let args: Args = argh::from_env(); - // let mut editor = Editor::new(args)?; - // editor.run()?; + editor::Editor::run()?; Ok(()) } -- cgit v1.2.3-70-g09d2