diff --git a/src/editor.rs b/src/editor.rs index c0b9d8b..134741f 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,48 +1,77 @@ -use std::io::{self, stdout}; +use crate::Terminal; use termion::event::Key; -use termion::input::TermRead; -use termion::raw::IntoRawMode; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct Editor { should_quit: bool, + terminal: Terminal, } impl Editor { pub fn run(&mut self) { - let _stdout = stdout().into_raw_mode().unwrap(); - loop { - if let Err(error) = self.process_keypress() { + if let Err(error) = self.refresh_screen() { die(error); } if self.should_quit { break; } + if let Err(error) = self.process_keypress() { + die(error); + } } } pub fn default() -> Self { - Self { should_quit: false } + Self { + should_quit: false, + terminal: Terminal::default().expect("Failed to initialize terminal"), + } + } + + fn refresh_screen(&self) -> Result<(), std::io::Error> { + Terminal::cursor_hide(); + Terminal::cursor_position(0, 0); + if self.should_quit { + Terminal::clear_screen(); + println!("Goodbye.\r"); + } else { + self.draw_rows(); + Terminal::cursor_position(0, 0); + } + Terminal::cursor_show(); + Terminal::flush() } fn process_keypress(&mut self) -> Result<(), std::io::Error> { - let pressed_key = read_key()?; + let pressed_key = Terminal::read_key()?; match pressed_key { Key::Ctrl('q') => self.should_quit = true, _ => (), } Ok(()) } -} -fn read_key() -> Result { - loop { - if let Some(key) = io::stdin().lock().keys().next() { - return key; + fn draw_rows(&self) { + let height = self.terminal.size().height; + + for row in 0..height - 1 { + Terminal::clear_current_line(); + + if row == height / 3 { + let welcome_message = format!("Hecto editor -- version {}", VERSION); + + let width = std::cmp::min(self.terminal.size() as usize, welcome_message.len()); + println!("{}\r", &welcome_message[..width]); + } else { + println!("~\r"); + } } } } fn die(e: std::io::Error) { + Terminal::clear_screen(); panic!(e); } diff --git a/src/main.rs b/src/main.rs index 8a00098..2d0d27b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ #![warn(clippy::all, clippy::pedantic)] mod editor; +mod terminal; use editor::Editor; +pub use terminal::Terminal; fn main() { Editor::default().run(); diff --git a/src/terminal.rs b/src/terminal.rs new file mode 100644 index 0000000..fb8435d --- /dev/null +++ b/src/terminal.rs @@ -0,0 +1,66 @@ +use std::io::{self, stdout, Write}; +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::{IntoRawMode, RawTerminal}; + +pub struct Size { + pub width: u16, + pub height: u16, +} + +pub struct Terminal { + size: Size, + _stdout: RawTerminal, +} + +impl Terminal { + pub fn default() -> Result { + let size = termion::terminal_size()?; + Ok(Self { + size: Size { + width: size.0, + height: size.1, + }, + _stdout: stdout().into_raw_mode()?, + }) + } + + pub fn size(&self) -> &Size { + &self.size + } + + pub fn clear_screen() { + print!("{}", termion::clear::All); + } + + pub fn cursor_position(x: u16, y: u16) { + let x = x.saturating_add(1); + let y = y.saturating_add(1); + + print!("{}", termion::cursor::Goto(x, y)); + } + + pub fn flush() -> Result<(), std::io::Error> { + io::stdout().flush() + } + + pub fn read_key() -> Result { + loop { + if let Some(key) = io::stdin().lock().keys().next() { + return key; + } + } + } + + pub fn cursor_hide() { + print!("{}", termion::cursor::Hide); + } + + pub fn cursor_show() { + print!("{}", termion::cursor::Show); + } + + pub fn clear_current_line() { + print!("{}", termion::clear::CurrentLine); + } +} \ No newline at end of file