From 493bf9c190f17bd58255c4218f5bc2a029415da3 Mon Sep 17 00:00:00 2001 From: shepherdma Date: Wed, 29 Nov 2023 23:38:55 +0800 Subject: [PATCH] feature: add document mod and welcome message --- hecto/src/document.rs | 20 +++++++++++++++ hecto/src/editor.rs | 58 +++++++++++++++++++++++++++++++++++++++---- hecto/src/main.rs | 5 +++- hecto/src/row.rs | 20 +++++++++++++++ hecto/src/terminal.rs | 17 +++++++++---- 5 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 hecto/src/document.rs create mode 100644 hecto/src/row.rs diff --git a/hecto/src/document.rs b/hecto/src/document.rs new file mode 100644 index 0000000..458e985 --- /dev/null +++ b/hecto/src/document.rs @@ -0,0 +1,20 @@ +use crate::Row; + +#[derive(Default)] +pub struct Document { + rows: Vec, +} + +impl Document { + pub fn open() -> Self{ + let mut rows = Vec::new(); + rows.push(Row::from("Hello, World!")); + Self { rows } + } + pub fn row(&self, index: usize) -> Option<&Row> { + self.rows.get(index) + } + pub fn is_empty(&self) -> bool { + self.rows.is_empty() + } +} diff --git a/hecto/src/editor.rs b/hecto/src/editor.rs index 9b4f0ce..1e442cd 100644 --- a/hecto/src/editor.rs +++ b/hecto/src/editor.rs @@ -1,9 +1,19 @@ +use crate::Row; +use crate::Document; use crate::Terminal; use termion::event::Key; +const VERSION: &str = env!("CARGO_PKG_VERSION"); +#[derive(Default)] +pub struct Position { + pub x: usize, + pub y: usize, +} pub struct Editor { should_quit: bool, terminal: Terminal, + cursor_position: Position, + document: Document, } fn die(e: &std::io::Error) { Terminal::clear_screen(); @@ -29,36 +39,74 @@ impl Editor { Self{ should_quit: false, terminal: Terminal::default().expect("Failed to initialize terminal"), + cursor_position: Position::default(), + document: Document::open(), } } fn process_keypress(&mut self) -> Result<(), std::io::Error> { let pressed_key = Terminal::read_key()?; match pressed_key { Key::Ctrl('q') => self.should_quit = true, + Key::Up | Key::Down | Key::Left | Key::Right => self.move_cursor(pressed_key), _ => (), } Ok(()) } + + fn move_cursor(&mut self, key: Key) { + let Position { mut y, mut x} = self.cursor_position; + match key { + Key::Up => y = y.saturating_sub(1), + Key::Down => y = y.saturating_add(1), + Key::Right => x = x.saturating_add(1), + Key::Left => x = x.saturating_sub(1), + _ => (), + } + self.cursor_position = Position { x, y } + } fn refresh_screen(&self) -> Result<(), std::io::Error> { // print!("\x1b[2J"); Terminal::cursor_hide(); Terminal::clear_screen(); - Terminal::cursor_position(0, 0); + Terminal::cursor_position(&Position::default()); if self.should_quit { println!("Goodbye.\r"); } else { self.draw_rows(); - Terminal::cursor_position(0, 0); + Terminal::cursor_position(&self.cursor_position); } Terminal::cursor_show(); Terminal::flush() } fn draw_rows(&self) { - for _ in 0..self.terminal.size().height - 1 { - println!("~\r"); + let height = self.terminal.size().height; + for terminal_row in 0..height - 1 { + Terminal::clear_current_line(); + if let Some(row) = self.document.row(terminal_row as usize) { + self.draw_row(row) + } else if self.document.is_empty() && terminal_row == height / 3 { + self.draw_welcome_message(); + } else { + println!("~\r"); + } } } - + fn draw_welcome_message(&self) { + let mut welcome_message = format!("Hecto editor --version{}", VERSION); + let width = self.terminal.size().width as usize; + let len = welcome_message.len(); + let padding = width.saturating_sub(len) / 2; + let spaces = " ".repeat(padding.saturating_sub(1)); + welcome_message = format!("~{}{}", spaces, welcome_message); + welcome_message.truncate(width); + println!("{}\r", welcome_message); + } + pub fn draw_row(&self, row: &Row) { + let start = 0; + let end = self.terminal.size().width as usize; + let row = row.render(start, end); + println!("{}\r", row); + } } diff --git a/hecto/src/main.rs b/hecto/src/main.rs index 15c2fb9..4348f57 100644 --- a/hecto/src/main.rs +++ b/hecto/src/main.rs @@ -1,10 +1,13 @@ #![warn(clippy::all, clippy::pedantic)] +mod row; mod editor; +mod document; mod terminal; use editor::Editor; pub use terminal::Terminal; - +pub use document::Document; +pub use row::Row; fn main() { diff --git a/hecto/src/row.rs b/hecto/src/row.rs new file mode 100644 index 0000000..6220d36 --- /dev/null +++ b/hecto/src/row.rs @@ -0,0 +1,20 @@ +use std::cmp; +pub struct Row { + string: String, +} + +impl From<&str> for Row { + fn from(slice: &str) -> Self { + Self { + string: String::from(slice), + } + } +} + +impl Row { + pub fn render(&self, start: usize, end: usize) -> String { + let end = cmp::min(end, self.string.len()); + let start = cmp::min(start, end); + self.string.get(start..end).unwrap_or_default().to_string() + } +} diff --git a/hecto/src/terminal.rs b/hecto/src/terminal.rs index 8a95c61..9c4195b 100644 --- a/hecto/src/terminal.rs +++ b/hecto/src/terminal.rs @@ -1,7 +1,8 @@ +use crate::editor::Position; use std::io::{self, stdout, Write}; use termion::event::Key; use termion::input::TermRead; -use termion::raw::{IntoRawMode, RawTerminal} +use termion::raw::{IntoRawMode, RawTerminal}; pub struct Size { pub width: u16, @@ -30,9 +31,12 @@ impl Terminal { 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); + pub fn cursor_position(position: &Position) { + let Position{mut x, mut y} = position; + x = x.saturating_add(1); + y = y.saturating_add(1); + let x = x as u16; + let y = y as u16; print!("{}", termion::cursor::Goto(x, y)); } pub fn read_key() -> Result { @@ -43,7 +47,7 @@ impl Terminal { } } pub fn flush() -> Result<(), std::io::Error> { - io::stdout::flush() + io::stdout().flush() } pub fn cursor_hide() { print!("{}", termion::cursor::Hide); @@ -51,5 +55,8 @@ impl Terminal { pub fn cursor_show() { print!("{}", termion::cursor::Show); } + pub fn clear_current_line() { + print!("{}", termion::clear::CurrentLine); + } }