diff --git a/Cargo.toml b/Cargo.toml index 7a90527..93a09e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snowy_libs" -version = "0.1.3" +version = "0.1.4" edition = "2024" [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 4fcf304..1c7b9dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,10 @@ use std::{ time::{Duration, Instant}, }; +use crate::multi_line::MultilineSession; + +pub mod multi_line; + /// This function creates a thread where it will print a progress bar based on the done count that you will update. /// /// Stops when done >= total @@ -21,6 +25,7 @@ pub fn start_progress_bar( size: u8, title: &str, update_time: Duration, + op: MultilineSession, ) -> JoinHandle<()> { assert!(size <= 10, "Size must be between 0 and 10"); let done = done.clone(); @@ -103,8 +108,14 @@ pub fn start_progress_bar( string.push(' '); } - print!("{string}"); - let _ = io::stdout().flush(); // Try to flush + // op(&string); + // if &op.is_some() { + // print!("{string}"); + // } + // print!("{string}"); + + op.update_linecl(0, &string); + // let _ = io::stdout().flush(); // Try to flush // cycles.push(started_at.elapsed().as_micros()); @@ -126,10 +137,7 @@ pub fn start_progress_bar( #[cfg(test)] mod tests { - use std::{ - thread, - time::{Duration, Instant}, - }; + use std::{thread, time::Duration}; use super::*; @@ -137,7 +145,22 @@ mod tests { fn test_progress() { let value = Arc::new(AtomicU64::from(0)); - let thread = start_progress_bar(&value, 511, 6, "Writing", Duration::from_millis(100)); + // fn print(str: &str) { + // println!("{}", str); + // } + + let mut silly = multi_line::start_multiline_session(); + silly.reserve_line(); + + let thread = start_progress_bar( + &value, + 511, + 6, + "Writing", + Duration::from_millis(100), + silly.clone(), + ); + thread::spawn(move || { thread::sleep(Duration::from_secs(1)); @@ -152,4 +175,46 @@ mod tests { println!("finished!"); } + + #[test] + fn test_multiline_printing() { + let mut silly = multi_line::start_multiline_session(); + + silly.println("silly"); + silly.println("silly2"); + silly.println("silly5"); + + thread::sleep(Duration::from_secs(1)); + + silly.reserve_line(); + thread::sleep(Duration::from_secs(1)); + + let temp_sily = silly.clone(); + thread::spawn(move || { + loop { + temp_sily.println("silly\nsilly"); + thread::sleep(Duration::from_millis(100)); + } + }); + + let temp_sily = silly.clone(); + thread::spawn(move || { + loop { + temp_sily.update_linecl(0, "sillyeaseasewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"); + thread::sleep(Duration::from_millis(10)); + } + }); + + // thread::sleep(Duration::from_secs(1)); + + // for i in 0..5 { + // silly.println("silly6"); + // silly.println("silly98"); + // } + silly.reserve_line(); + silly.update_line(1, "hihidfwaflafwjklfwafilawfyabfilwaufuioawfi;ahfoawfhawupifoaiwfuioawofuahfuiawowfahpfawoawfiuhwa"); + silly.println("silly6"); + + thread::sleep(Duration::from_secs(10)); + } } diff --git a/src/multi_line.rs b/src/multi_line.rs new file mode 100644 index 0000000..3af455e --- /dev/null +++ b/src/multi_line.rs @@ -0,0 +1,149 @@ +use std::{ + io::{self, Write}, + string, + sync::{ + Arc, + atomic::{AtomicU8, Ordering}, + mpsc::{self, Receiver, Sender}, + }, + thread, +}; + +#[derive(Clone, Debug)] +pub struct MultilineSession { + // sender: Sender, + reserved_lines: Arc, +} + +// Clear currently selected line: \x1b[2K +// Delete currently selected line: \x1b[1M +// Create new line at currently selected line: \x1b[1L +// Move up any line: \x1b[#A +// Move up to beginning of any line: \x1b[#F +// Move down any line: \x1b[#B +// Move down to beginning of any line: \x1b[#E + +impl MultilineSession { + // pub fn print>(&self, row: u16, text: T) { + // let to_print = ToPrint { + // row, + // text: text.into(), + // clear: false, + // }; + + // self.sender.send(to_print).unwrap() + // } + // pub fn printcl>(&self, row: u16, text: T) { + // let to_print = ToPrint { + // row, + // text: text.into(), + // clear: true, + // }; + + // self.sender.send(to_print).unwrap() + // } + + pub fn println>(&self, text: T) { + let reserved_lines = self.reserved_lines.load(Ordering::Relaxed); + + if reserved_lines == 0 { + println!("{}", text.into()); + } else { + print!( + "\n\x1b[{}F\x1b[1L{}\x1b[{}B", + reserved_lines, + text.into(), + reserved_lines + ); + let _ = io::stdout().flush(); + } + } + + pub fn reserve_line(&mut self) { + let reserved_lines = self.reserved_lines.load(Ordering::Relaxed); + + if reserved_lines == 0 { + println!(); + } else { + print!("\n\x1b[{}A\x1b[1L\x1b[{}B", reserved_lines, reserved_lines); // create a new line there :3 + let _ = io::stdout().flush(); + } + + self.reserved_lines.fetch_add(1, Ordering::Relaxed); + } + + pub fn update_line>(&self, num: u8, text: T) { + let reserved_lines = self.reserved_lines.load(Ordering::Relaxed); + + if reserved_lines >= num { + if num == 0 { + print!("{}", text.into()); + let _ = io::stdout().flush(); + } else { + print!("\x1b[{}F{}\x1b[{}B", num, text.into(), num); + let _ = io::stdout().flush(); + } + } + } + + pub fn update_linecl>(&self, num: u8, text: T) { + let reserved_lines = self.reserved_lines.load(Ordering::Relaxed); + + if reserved_lines >= num { + if num == 0 { + print!("\r\x1b[2K{}", text.into()); + let _ = io::stdout().flush(); + } else { + print!("\x1b[{}F\r\x1b[2K{}\x1b[{}B", num, text.into(), num); + let _ = io::stdout().flush(); + } + } + } + // pub fn set_bottom(&mut self, text: &str) { + // self.bottom = text.to_string(); + // print!("\r\x1b[2K{}", self.bottom); + // let _ = io::stdout().flush(); + // } +} + +// struct ToPrint { +// /// This is the row from the lowest one. So the bottom of the terminal would be row 0 +// row: u16, +// text: String, +// clear: bool, +// } + +pub fn start_multiline_session() -> MultilineSession { + // let (sender, reciever): (Sender, Receiver) = mpsc::channel(); + + // // Start a new thread for recieving the messages so we can print them in the correct order without race conditions + // thread::spawn(move || { + // loop { + // if let Ok(recv) = reciever.recv() { + // let mut extra_opts = String::new(); + + // if recv.clear { + // extra_opts.push_str("\x1b[2K"); + // } + + // if recv.row == 0 { + // print!("\r{}{}", extra_opts, recv.text); + // let _ = io::stdout().flush(); + // } else { + // print!( + // "\x1b[{}F{}{}\x1b[{}B", + // recv.row, extra_opts, recv.text, recv.row + // ); + // let _ = io::stdout().flush(); + // } + // } + // } + // }); + + // MultilineSession { + // bottom: "".to_string(), + // } + MultilineSession { + reserved_lines: Arc::new(AtomicU8::new(0)), + } +}