diff --git a/Cargo.lock b/Cargo.lock index 943f46e..aa77f7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -31,17 +20,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "crossterm" version = "0.25.0" @@ -67,29 +45,13 @@ dependencies = [ "winapi", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "lambda" version = "0.1.0" dependencies = [ - "colored", "crossterm", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.137" diff --git a/Cargo.toml b/Cargo.toml index 43281cd..23443e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,3 @@ edition = "2021" [dependencies] crossterm = "0.25" -colored = "2" diff --git a/TODO.md b/TODO.md index 072c505..f680904 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,4 @@ -- [ ] Remove colored in favour of crossterm's styling -- [ ] Use &str for writing text -- [ ] Make sure borrows for Screen and Editor are correct -- [ ] Fix small terminal screen issues +- [X] Remove colored in favour of crossterm's styling +- [X] Use &str for writing text -> can't really be done +- [X] Make sure borrows for Screen and Editor are correct +- [X] Fix small terminal screen issues diff --git a/src/editor.rs b/src/editor.rs index bc2f5d7..ecb4a2c 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,10 +1,14 @@ pub struct Config<'a> { pub logo: &'a str, + pub friendly_name: &'a str, } impl<'a> Config<'a> { pub fn new() -> Self { - Self { logo: "λ" } + Self { + logo: "λ", + friendly_name: "Lambda", + } } } @@ -14,6 +18,7 @@ pub struct Buffer<'a> { pub path: &'a str, } +#[allow(dead_code)] pub enum Mode { Normal, Insert, diff --git a/src/terminal.rs b/src/terminal.rs index d1f7dc3..2d32374 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -105,6 +105,19 @@ impl Screen { // Terminal functions and methods for managing the terminal impl Screen { + pub fn refresh(&mut self) -> Result<(), ErrorKind>{ + // Clear the screen + Screen::clear(); + + // Update the screen dimensions + let size = terminal::size()?; + self.size.width = size.0 as usize; + self.size.height = size.1 as usize; + + // Return Ok if was successful + Ok(()) + } + pub fn enter(&mut self) { // Hide the cursor self.cursor.hide(); @@ -123,19 +136,19 @@ impl Screen { terminal::disable_raw_mode().unwrap(); } - pub fn write(text: String) { - // Writes a line to a current cursor position - execute!(stdout(), Print(text)).unwrap(); - } - pub fn clear() { // Clears the terminal screen execute!(stdout(), terminal::Clear(terminal::ClearType::All)).unwrap(); } + pub fn write(text: &str) { + // Writes a line to a current cursor position + execute!(stdout(), Print(text)).unwrap(); + } + pub fn write_at(&mut self, text: String, position: Coords) { // Writes a line at a set of coordinates self.cursor.move_to(position); - Screen::write(text); + Screen::write(&text); } } diff --git a/src/tui.rs b/src/tui.rs index 148acc2..c6a5343 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,52 +1,74 @@ use crate::editor::Editor; use crate::terminal::{Coords, Screen}; -use colored::Colorize; +use crossterm::style::Stylize; use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers}; -pub fn draw_status(screen: &mut Screen, editor: &Editor) { +// Utils +fn with_spaces(text: &str) -> String { + format!(" {} ", text) +} +fn calc_x(screen_width: usize, item_length: usize) -> usize { + (screen_width / 2) - (item_length / 2) +} +fn longest_element_in_array(elements: Vec<&str>) -> usize { + elements.iter().max_by_key(|x: &&&str| x.len()).unwrap().len() +} + +pub fn draw_status(screen: &mut Screen, editor: &Editor) -> Result<(), ()> { // Calculate where to draw the status bar let status_height = screen.size.height - 2; // Get the editor logo from the config - let editor_logo = &format!(" {} ", editor.config.logo) as &str; - // Write the editor logo - screen.write_at( - editor_logo.bright_yellow().bold().reversed().to_string(), - Coords::from(0, status_height), - ); - + let editor_logo = &with_spaces(editor.config.logo) as &str; // Get the current mode into a string - let mode_string = &format!(" {} ", editor.mode.as_str()) as &str; - // Calculate where to write the current mode - let x = editor_logo.len() - 1; - // Write the current mode - screen.write_at( - mode_string.green().bold().reversed().to_string(), - Coords::from(x, status_height), - ); - + let mode_string = &with_spaces(editor.mode.as_str()) as &str; // Get the current open file name - let file_name = &format!(" {} ", editor.buffer.name) as &str; - // Calculate where to write the file name - let x = x + mode_string.len(); - // Write the current file name - screen.write_at( - file_name.magenta().bold().reversed().to_string(), - Coords::from(x, status_height), - ); + let file_name = &with_spaces(editor.buffer.name) as &str; - // Draw the rest of the status bar - let x = x + file_name.len(); - screen.write_at( - " ".repeat(screen.size.width - x).reversed().to_string(), - Coords::from(x, status_height), - ); + // Calculate the total length of all the status bar components + let total_length = editor_logo.len() + mode_string.len() + file_name.len() + 1; + + // If the screen isn't wide enough, panic as we can't draw the status bar + if screen.size.width < total_length { + Err(()) + + } else { + // Write the editor logo + screen.write_at( + editor_logo.yellow().bold().reverse().to_string(), + Coords::from(0, status_height), + ); + + // Calculate where to write the current mode + let x = editor_logo.len() - 1; + // Write the current mode + screen.write_at( + mode_string.green().bold().reverse().to_string(), + Coords::from(x, status_height), + ); + // Calculate where to write the file name + let x = x + mode_string.len(); + // Write the current file name + screen.write_at( + file_name.magenta().bold().reverse().to_string(), + Coords::from(x, status_height), + ); + + // Draw the rest of the status bar + let x = x + file_name.len(); + screen.write_at( + " ".repeat(screen.size.width - x).reverse().to_string(), + Coords::from(x, status_height), + ); + + Ok(()) + } } pub fn draw_welcome(screen: &mut Screen, editor: &Editor) { // The welcome message - let message: [&str; 6] = [ - &"λ Lambda".yellow() as &str, + let title = format!("{} {}", editor.config.logo, editor.config.friendly_name); + let message: [&str; 5] = [ "Hackable text editor for nerds", "", "Type :help to open the README.md document", @@ -54,28 +76,40 @@ pub fn draw_welcome(screen: &mut Screen, editor: &Editor) { "Type :q! or to quit lambda", ]; - // The starting y position in the centre of the screen - let mut y = (screen.size.height / 2) - (message.len() / 2) - 2; + // If the screen is big enough, we can draw + if screen.size.width > longest_element_in_array(message.to_vec()) && screen.size.height > message.len() + 4 { + // The starting y position in the centre of the screen + let mut y = (screen.size.height / 2) - (message.len() / 2) - 2; - for line in message { - // Each line has different width so requires a different x position to center it - let x = (screen.size.width / 2) - (line.len() / 2); + // Calculate where to place the title + let x = calc_x(screen.size.width, title.len()); - // For each line we move downwards so increment y - y += 1; + // Write the title to the screen + screen.write_at(title.yellow().to_string(), Coords::from(x, y)); - // Write the line to the screen at position (x, y) - screen.write_at(line.to_string(), Coords::from(x, y)); + for line in message { + // Each line has different width so requires a different x position to center it + let x = calc_x(screen.size.width, line.len()) ; + + // For each line we move downwards so increment y + y += 1; + + // Write the line to the screen at position (x, y) + screen.write_at(line.to_string(), Coords::from(x, y)); + } } } pub fn start(screen: &mut Screen, editor: Editor) { loop { + // Refresh the screen + screen.refresh().unwrap(); + // Draw the welcome message draw_welcome(screen, &editor); // Draw the status bar - draw_status(screen, &editor); + draw_status(screen, &editor).unwrap(); // Check for any key presses match read().unwrap() {