diff --git a/Cargo.lock b/Cargo.lock index 2f50dc7..814ca24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" -version = "2.6.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" @@ -22,15 +22,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crossterm" -version = "0.28.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788" dependencies = [ "bitflags", "crossterm_winapi", + "libc", "mio", "parking_lot", - "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -38,21 +38,20 @@ dependencies = [ [[package]] name = "crossterm_winapi" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" dependencies = [ "winapi", ] [[package]] -name = "errno" -version = "0.3.10" +name = "instant" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "libc", - "windows-sys 0.59.0", + "cfg-if", ] [[package]] @@ -61,12 +60,6 @@ version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "lock_api" version = "0.4.12" @@ -85,61 +78,69 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "mio" -version = "1.0.3" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", - "wasi", - "windows-sys 0.52.0", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ + "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", + "instant", "libc", "redox_syscall", "smallvec", - "windows-targets", + "winapi", ] [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] -[[package]] -name = "rustix" -version = "0.38.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - [[package]] name = "rustpad" version = "0.1.0" @@ -189,12 +190,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "winapi" version = "0.3.9" @@ -216,85 +211,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index d558459..4245648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -crossterm = "0.28.1" +crossterm = "0.21.0" diff --git a/src/editor.rs b/src/editor.rs deleted file mode 100644 index 772ae75..0000000 --- a/src/editor.rs +++ /dev/null @@ -1,160 +0,0 @@ -use core::cmp::min; -use crossterm::event::{ - read, - Event::{self, Key}, - KeyCode, KeyEvent, KeyEventKind, KeyModifiers, -}; -use std::io::Error; -mod terminal; -use terminal::{Position, Size, Terminal}; - -const NAME: &str = env!("CARGO_PKG_NAME"); -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[derive(Copy, Clone, Default)] -struct Location { - x: usize, - y: usize, -} - -#[derive(Default)] -pub struct Editor { - should_quit: bool, - location: Location, -} - -impl Editor { - pub fn run(&mut self) { - Terminal::initialize().unwrap(); - let result = self.repl(); - Terminal::terminate().unwrap(); - result.unwrap(); - } - - fn repl(&mut self) -> Result<(), Error> { - loop { - self.refresh_screen()?; - if self.should_quit { - break; - } - let event = read()?; - self.evaluate_event(&event)?; - } - Ok(()) - } - fn move_point(&mut self, key_code: KeyCode) -> Result<(), Error> { - let Location { mut x, mut y } = self.location; - let Size { height, width } = Terminal::size()?; - match key_code { - KeyCode::Up => { - y = y.saturating_sub(1); - } - KeyCode::Down => { - y = min(height.saturating_sub(1), y.saturating_add(1)); - } - KeyCode::Left => { - x = x.saturating_sub(1); - } - KeyCode::Right => { - x = min(width.saturating_sub(1), x.saturating_add(1)); - } - KeyCode::PageUp => { - y = 0; - } - KeyCode::PageDown => { - y = height.saturating_sub(1); - } - KeyCode::Home => { - x = 0; - } - KeyCode::End => { - x = width.saturating_sub(1); - } - _ => (), - } - self.location = Location { x, y }; - Ok(()) - } - fn evaluate_event(&mut self, event: &Event) -> Result<(), Error> { - if let Key(KeyEvent { - code, - modifiers, - kind: KeyEventKind::Press, - .. - }) = event - { - match code { - KeyCode::Char('q') if *modifiers == KeyModifiers::CONTROL => { - self.should_quit = true; - } - KeyCode::Up - | KeyCode::Down - | KeyCode::Left - | KeyCode::Right - | KeyCode::PageDown - | KeyCode::PageUp - | KeyCode::End - | KeyCode::Home => { - self.move_point(*code)?; - } - _ => (), - } - } - Ok(()) - } - fn refresh_screen(&self) -> Result<(), Error> { - Terminal::hide_caret()?; - Terminal::move_caret_to(Position::default())?; - if self.should_quit { - Terminal::clear_screen()?; - Terminal::print("Goodbye.\r\n")?; - } else { - Self::draw_rows()?; - Terminal::move_caret_to(Position { - col: self.location.x, - row: self.location.y, - })?; - } - - Terminal::show_caret()?; - Terminal::execute()?; - Ok(()) - } - fn draw_welcome_message() -> Result<(), Error> { - let mut welcome_message = format!("{NAME} editor -- version {VERSION}"); - let width = Terminal::size()?.width; - let len = welcome_message.len(); - // we allow this since we don't care if our welcome message is put _exactly_ in the middle. - // it's allowed to be a bit to the left or right. - #[allow(clippy::integer_division)] - let padding = (width.saturating_sub(len)) / 2; - - let spaces = " ".repeat(padding.saturating_sub(1)); - welcome_message = format!("~{spaces}{welcome_message}"); - welcome_message.truncate(width); - Terminal::print(welcome_message)?; - Ok(()) - } - fn draw_empty_row() -> Result<(), Error> { - Terminal::print("~")?; - Ok(()) - } - fn draw_rows() -> Result<(), Error> { - let Size { height, .. } = Terminal::size()?; - for current_row in 0..height { - Terminal::clear_line()?; - // we allow this since we don't care if our welcome message is put _exactly_ in the middle. - // it's allowed to be a bit up or down - #[allow(clippy::integer_division)] - if current_row == height / 3 { - Self::draw_welcome_message()?; - } else { - Self::draw_empty_row()?; - } - if current_row.saturating_add(1) < height { - Terminal::print("\r\n")?; - } - } - Ok(()) - } -} \ No newline at end of file diff --git a/src/editor/cursor_controller.rs b/src/editor/cursor_controller.rs new file mode 100644 index 0000000..b4de3e4 --- /dev/null +++ b/src/editor/cursor_controller.rs @@ -0,0 +1,83 @@ +use std::cmp; +use std::cmp::Ordering; + +use crossterm::event::KeyCode; +use crate::editor::EditorRows; + + +pub struct CursorController { + pub cursor_x: usize, + pub cursor_y: usize, + screen_rows: usize, + screen_columns: usize, + pub row_offset: usize, + pub column_offset:usize +} + +impl CursorController { + pub fn new(win_size: (usize, usize)) -> CursorController { + Self { + cursor_x: 0, + cursor_y: 0, + screen_columns: win_size.0, + screen_rows: win_size.1, + row_offset: 0, + column_offset:0 + } + } + + pub fn scroll(&mut self) { + self.row_offset = cmp::min(self.row_offset, self.cursor_y); + if self.cursor_y >= self.row_offset + self.screen_rows { + self.row_offset = self.cursor_y - self.screen_rows + 1; + } + self.column_offset = cmp::min(self.column_offset, self.cursor_x); + if self.cursor_x >= self.column_offset + self.screen_columns { + self.column_offset = self.cursor_x - self.screen_columns + 1; + } + } + + pub fn move_cursor(&mut self, direction: KeyCode, editor_rows: &EditorRows) { + let number_of_rows = editor_rows.number_of_rows(); + match direction { + KeyCode::Up => { + self.cursor_y = self.cursor_y.saturating_sub(1); + } + KeyCode::Left => { + if self.cursor_x != 0 { + self.cursor_x -= 1; + } + else if self.cursor_y > 0 { + self.cursor_y -= 1; + self.cursor_x = editor_rows.get_row(self.cursor_y).len(); + } + } + KeyCode::Down => { + if self.cursor_y < number_of_rows { + self.cursor_y += 1; + } + } + KeyCode::Right => { + if self.cursor_y < number_of_rows { + match self.cursor_x.cmp(&editor_rows.get_row(self.cursor_y).len()) { + Ordering::Less => self.cursor_x += 1, + Ordering::Equal => { + self.cursor_y += 1; + self.cursor_x = 0 + } + _ => {} + } + } + } + KeyCode::End => self.cursor_x = self.screen_columns - 1, + KeyCode::Home => self.cursor_x = 0, + _ => unimplemented!(), + } + let row_len = if self.cursor_y < number_of_rows { + editor_rows.get_row(self.cursor_y).len() + } else { + 0 + }; + self.cursor_x = cmp::min(self.cursor_x, row_len); + } +} diff --git a/src/editor/editor_contents.rs b/src/editor/editor_contents.rs new file mode 100644 index 0000000..011ac7c --- /dev/null +++ b/src/editor/editor_contents.rs @@ -0,0 +1,41 @@ +use std::io; +use std::io::stdout; + +pub struct EditorContents { + content: String, +} + +impl EditorContents { + pub fn new() -> Self { + Self { + content: String::new(), + } + } + + pub fn push(&mut self, ch: char) { + self.content.push(ch) + } + + pub fn push_str(&mut self, string: &str) { + self.content.push_str(string) + } +} + +impl io::Write for EditorContents { + fn write(&mut self, buf: &[u8]) -> io::Result { + match std::str::from_utf8(buf) { + Ok(s) => { + self.content.push_str(s); + Ok(s.len()) + } + Err(_) => Err(io::ErrorKind::WriteZero.into()), + } + } + + fn flush(&mut self) -> io::Result<()> { + let out = write!(stdout(), "{}", self.content); + stdout().flush()?; + self.content.clear(); + out + } +} \ No newline at end of file diff --git a/src/editor/editor_rows.rs b/src/editor/editor_rows.rs new file mode 100644 index 0000000..1078e5e --- /dev/null +++ b/src/editor/editor_rows.rs @@ -0,0 +1,34 @@ +use std::{env, fs}; +use std::path::Path; + +pub struct EditorRows { + row_contents: Vec>, +} + +impl EditorRows { + pub fn new() -> Self { + let mut arg = env::args(); + + match arg.nth(1) { + None => Self { + row_contents: Vec::new(), + }, + Some(file) => Self::from_file(file.as_ref()), + } + } + + fn from_file(file: &Path) -> Self { + let file_contents = fs::read_to_string(file).expect("Unable to read file"); + Self { + row_contents: file_contents.lines().map(|it| it.into()).collect(), + } + } + + pub fn number_of_rows(&self) -> usize { + self.row_contents.len() + } + + pub fn get_row(&self, at:usize) -> &str { + &self.row_contents[at] + } +} \ No newline at end of file diff --git a/src/editor/mod.rs b/src/editor/mod.rs new file mode 100644 index 0000000..b17a576 --- /dev/null +++ b/src/editor/mod.rs @@ -0,0 +1,160 @@ +mod editor_contents; +mod editor_rows; +mod cursor_controller; +mod reader; + +use editor_contents::EditorContents; +use editor_rows::EditorRows; +use cursor_controller::CursorController; +use reader::Reader; +use crossterm::event::*; +use crossterm::terminal::ClearType; +use crossterm::{cursor, execute, queue, terminal}; +use std::{cmp, env}; +use std::io::{stdout, Write}; + +struct Output { + win_size: (usize, usize), + editor_contents: EditorContents, + cursor_controller: CursorController, + editor_rows: EditorRows, +} + +impl Output { + fn new() -> Self { + let win_size = terminal::size() + .map(|(x, y)| (x as usize, y as usize)) + .unwrap(); + Self { + win_size, + editor_contents: EditorContents::new(), + cursor_controller: CursorController::new(win_size), + editor_rows: EditorRows::new(), + } + } + + fn clear_screen() -> crossterm::Result<()> { + execute!(stdout(), terminal::Clear(ClearType::All))?; + execute!(stdout(), cursor::MoveTo(0, 0)) + } + + fn move_cursor(&mut self, direction: KeyCode) { + self.cursor_controller + .move_cursor(direction, &self.editor_rows); + } + + fn draw_rows(&mut self) { + let screen_rows = self.win_size.1; + let screen_columns = self.win_size.0; + for i in 0..screen_rows { + let file_row = i + self.cursor_controller.row_offset; + if file_row >= self.editor_rows.number_of_rows() { + if self.editor_rows.number_of_rows() == 0 && i == screen_rows / 3 { + let name = env::var("CARGO_PKG_NAME").unwrap_or_else(|_| "Unknown".to_string()); + let version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "Unknown".to_string()); + let mut welcome = format!("{} --- Version {}", name, version); + if welcome.len() > screen_columns { + welcome.truncate(screen_columns) + } + let mut padding = (screen_columns - welcome.len()) / 2; + if padding != 0 { + self.editor_contents.push('~'); + padding -= 1 + } + (0..padding).for_each(|_| self.editor_contents.push(' ')); + self.editor_contents.push_str(&welcome); + } else { + self.editor_contents.push('~'); + } + } else { + let row = self.editor_rows.get_row(file_row); + let column_offset = self.cursor_controller.column_offset; + let len = cmp::min(row.len().saturating_sub(column_offset), screen_columns); + let start = if len == 0 { 0 } else { column_offset }; + self.editor_contents + .push_str(&row[start..start + len]); + } + queue!( + self.editor_contents, + terminal::Clear(ClearType::UntilNewLine) + ) + .unwrap(); + if i < screen_rows - 1 { + self.editor_contents.push_str("\r\n"); + } + } + } + + fn refresh_screen(&mut self) -> crossterm::Result<()> { + self.cursor_controller.scroll(); + queue!(self.editor_contents, cursor::Hide, cursor::MoveTo(0, 0))?; + self.draw_rows(); + let cursor_x = self.cursor_controller.cursor_x - self.cursor_controller.column_offset; + let cursor_y = self.cursor_controller.cursor_y - self.cursor_controller.row_offset; + queue!( + self.editor_contents, + cursor::MoveTo(cursor_x as u16, cursor_y as u16), + cursor::Show + )?; + self.editor_contents.flush() + } +} + +pub struct Editor { + reader: Reader, + output: Output, +} + +impl Editor { + pub fn new() -> Self { + Self { + reader: Reader, + output: Output::new(), + } + } + + fn process_keypress(&mut self) -> crossterm::Result { + match self.reader.read_key()? { + KeyEvent { + code: KeyCode::Char('q'), + modifiers: KeyModifiers::CONTROL, + } => return Ok(false), + KeyEvent { + code: + direction + @ + (KeyCode::Up + | KeyCode::Down + | KeyCode::Left + | KeyCode::Right + | KeyCode::Home //add + | KeyCode::End), //add + modifiers: KeyModifiers::NONE, + } => self.output.move_cursor(direction), + KeyEvent { + code: val @ (KeyCode::PageUp | KeyCode::PageDown), + modifiers: KeyModifiers::NONE, + } => (0..self.output.win_size.1).for_each(|_| { + self.output.move_cursor(if matches!(val, KeyCode::PageUp) { + KeyCode::Up + } else { + KeyCode::Down + }); + }), + _ => {} + } + Ok(true) + } + + pub fn run(&mut self) -> crossterm::Result { + self.output.refresh_screen()?; + self.process_keypress() + } +} + +impl Drop for Editor { + fn drop(&mut self) { + terminal::disable_raw_mode().expect("Unable to disable raw mode"); + Output::clear_screen().expect("Error"); + } +} \ No newline at end of file diff --git a/src/editor/reader.rs b/src/editor/reader.rs new file mode 100644 index 0000000..dfe7a3b --- /dev/null +++ b/src/editor/reader.rs @@ -0,0 +1,16 @@ +use std::time::Duration; +use crossterm::event::{self, Event, KeyEvent}; + +pub struct Reader; + +impl Reader { + pub fn read_key(&self) -> crossterm::Result { + loop { + if event::poll(Duration::from_millis(500))? { + if let Event::Key(event) = event::read()? { + return Ok(event); + } + } + } + } +} \ No newline at end of file diff --git a/src/editor/terminal.rs b/src/editor/terminal.rs deleted file mode 100644 index 1fb5d26..0000000 --- a/src/editor/terminal.rs +++ /dev/null @@ -1,90 +0,0 @@ -use core::fmt::Display; -use crossterm::cursor::{Hide, MoveTo, Show}; -use crossterm::style::Print; -use crossterm::terminal::{disable_raw_mode, enable_raw_mode, size, Clear, ClearType}; -use crossterm::{queue, Command}; -use std::io::{stdout, Error, Write}; - -#[derive(Copy, Clone)] -pub struct Size { - pub height: usize, - pub width: usize, -} -#[derive(Copy, Clone, Default)] -pub struct Position { - pub col: usize, - pub row: usize, -} -/// Represents the Terminal. -/// Edge Case for platforms where `usize` < `u16`: -/// Regardless of the actual size of the Terminal, this representation -/// only spans over at most `usize::MAX` or `u16::size` rows/columns, whichever is smaller. -/// Each size returned truncates to min(`usize::MAX`, `u16::MAX`) -/// And should you attempt to set the caret out of these bounds, it will also be truncated. -pub struct Terminal; - -impl Terminal { - pub fn terminate() -> Result<(), Error> { - Self::execute()?; - disable_raw_mode()?; - Ok(()) - } - pub fn initialize() -> Result<(), Error> { - enable_raw_mode()?; - Self::clear_screen()?; - Self::execute()?; - Ok(()) - } - pub fn clear_screen() -> Result<(), Error> { - Self::queue_command(Clear(ClearType::All))?; - Ok(()) - } - pub fn clear_line() -> Result<(), Error> { - Self::queue_command(Clear(ClearType::CurrentLine))?; - Ok(()) - } - /// Moves the caret to the given Position. - /// # Arguments - /// * `Position` - the `Position`to move the caret to. Will be truncated to `u16::MAX` if bigger. - pub fn move_caret_to(position: Position) -> Result<(), Error> { - // clippy::as_conversions: See doc above - #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] - Self::queue_command(MoveTo(position.col as u16, position.row as u16))?; - Ok(()) - } - pub fn hide_caret() -> Result<(), Error> { - Self::queue_command(Hide)?; - Ok(()) - } - pub fn show_caret() -> Result<(), Error> { - Self::queue_command(Show)?; - Ok(()) - } - pub fn print(string: T) -> Result<(), Error> { - Self::queue_command(Print(string))?; - Ok(()) - } - - /// Returns the current size of this Terminal. - /// Edge Case for systems with `usize` < `u16`: - /// * A `Size` representing the terminal size. Any coordinate `z` truncated to `usize` if `usize` < `z` < `u16` - pub fn size() -> Result { - let (width_u16, height_u16) = size()?; - // clippy::as_conversions: See doc above - #[allow(clippy::as_conversions)] - let height = height_u16 as usize; - // clippy::as_conversions: See doc above - #[allow(clippy::as_conversions)] - let width = width_u16 as usize; - Ok(Size { height, width }) - } - pub fn execute() -> Result<(), Error> { - stdout().flush()?; - Ok(()) - } - - fn queue_command(command: T) -> Result<(), Error> { - queue!(stdout(), command)?; - Ok(()) - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b7d81fb..f9f1274 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ -#![warn(clippy::all, clippy::pedantic)] mod editor; -use editor::Editor; -fn main() { - Editor::default().run(); +use crossterm::terminal; + +fn main() -> crossterm::Result<()> { + terminal::enable_raw_mode()?; + let mut editor = editor::Editor::new(); + while editor.run()? {} + Ok(()) } \ No newline at end of file