editor.rs now uses crossterm

pull/1/head
Jan Hrastnik 4 years ago committed by Blaž Hrastnik
parent 073fe61264
commit 19643446cd

@ -1,287 +1,69 @@
use anyhow::Error; use std::io::{self, stdout, Write};
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 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 impl Editor {
pub struct MainScreen {} pub fn read_char() -> Result<char> {
loop {
impl MainScreen { if let Ok(Event::Key(KeyEvent {
pub fn new() -> Self { code: KeyCode::Char(c),
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,
.. ..
})) => { })) = event::read()
self.text.push_str("\r\n"); {
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 pub fn print_events() -> Result<()> {
fn render(&mut self, args: &mut RenderArgs) { loop {
args.surface // Handle key events
.add_change(Change::ClearScreen(ColorAttribute::Default)); match Editor::read_char()? {
'h' => execute!(io::stdout(), cursor::MoveLeft(1))?,
// args.surface 'j' => execute!(io::stdout(), cursor::MoveDown(1))?,
// .add_change(Change::Attribute(AttributeChange::Foreground( 'k' => execute!(io::stdout(), cursor::MoveUp(1))?,
// ColorAttribute::TrueColorWithPaletteFallback( 'l' => execute!(io::stdout(), cursor::MoveRight(1))?,
// RgbColor::new(0x11, 0x00, 0xFF), 'q' => {
// AnsiColor::Purple.into(), execute!(
// ), io::stdout(),
// ))); style::ResetColor,
let (_width, height) = args.surface.dimensions(); cursor::Show,
terminal::LeaveAlternateScreen
for line in self.buffer.contents.lines_at(self.first_line).take(height) { )?;
args.surface break;
.add_change(unsafe { String::from_utf8_unchecked(line.bytes().collect()) }); }
args.surface.add_change("\r"); _ => println!("use 'q' to quit."),
} }
// 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<SystemTerminal>,
buffer: Option<Buffer>,
}
impl Editor {
pub fn new(mut args: Args) -> Result<Self, Error> {
// 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)?;
} }
Ok(editor)
}
pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
let buffer = Buffer::load(path)?;
self.buffer = Some(buffer);
Ok(()) Ok(())
} }
pub fn run(&mut self) -> Result<(), Error> { pub fn run() -> Result<()> {
// Start with an empty string; typing into the app will enable_raw_mode()?;
// 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);
loop { // used for clearing the screen
ui.process_event_queue()?; execute!(io::stdout(), terminal::EnterAlternateScreen)?;
println!("{}", HELP);
// After updating and processing all of the widgets, compose them let mut stdout = stdout();
// and render them to the screen. if let Err(e) = Editor::print_events() {
if ui.render_to_screen(buf)? { println!("Error: {:?}\r", e);
// 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;
}
}
}
} }
// After we've stopped the full screen raw terminal, disable_raw_mode()
// 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
} }
} }

@ -1,9 +1,10 @@
#![allow(unused)] #![allow(unused)]
// mod editor; // mod editor;
// mod component; // mod component;
mod editor;
mod keymap; mod keymap;
// use editor::Editor; use editor::Editor;
use argh::FromArgs; use argh::FromArgs;
use std::path::PathBuf; use std::path::PathBuf;
@ -19,9 +20,8 @@ pub struct Args {
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
let args: Args = argh::from_env(); let args: Args = argh::from_env();
// let mut editor = Editor::new(args)?;
// editor.run()?; editor::Editor::run()?;
Ok(()) Ok(())
} }

Loading…
Cancel
Save