diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000..f502140 --- /dev/null +++ b/src/common.rs @@ -0,0 +1,19 @@ +use console::Term; + +/// Returns a tuple with the height of the output and the number of lines that overflow the terminal width +pub fn get_height(term: &Term, output: String) -> (usize, usize) { + let max_width = term.size().1 as usize; + let height = output.lines().count() - 1; + // calculate potential overflow of lines overflowing terminal width + let overflow = output + .lines() + .map(|l| { + if l.chars().count() > max_width { + l.chars().count() / max_width + } else { + 0 + } + }) + .sum::(); + (height, overflow) +} diff --git a/src/confirm.rs b/src/confirm.rs index 23d836e..09ea934 100644 --- a/src/confirm.rs +++ b/src/confirm.rs @@ -4,8 +4,8 @@ use std::io::Write; use console::{Key, Term}; use termcolor::{Buffer, WriteColor}; -use crate::theme; use crate::theme::Theme; +use crate::{common, theme}; /// Select multiple options from a list /// @@ -34,6 +34,7 @@ pub struct Confirm<'a> { pub selected: bool, term: Term, height: usize, + overflow: usize, } impl<'a> Confirm<'a> { @@ -48,6 +49,7 @@ impl<'a> Confirm<'a> { negative: "No".to_string(), selected: true, height: 0, + overflow: 0, } } @@ -88,7 +90,6 @@ impl<'a> Confirm<'a> { loop { self.clear()?; let output = self.render()?; - self.height = output.lines().count() - 1; self.term.write_all(output.as_bytes())?; self.term.flush()?; match self.term.read_key()? { @@ -96,17 +97,22 @@ impl<'a> Confirm<'a> { Key::ArrowRight | Key::Char('l') => self.handle_right(), Key::Char(c) if c == affirmative_char => { self.selected = true; + self.update_height(output)?; return self.handle_submit(); } Key::Char(c) if c == negative_char => { self.selected = false; + self.update_height(output)?; return self.handle_submit(); } Key::Enter => { + self.update_height(output)?; return self.handle_submit(); } _ => {} } + + self.update_height(output)?; } } @@ -193,7 +199,15 @@ impl<'a> Confirm<'a> { Ok(std::str::from_utf8(out.as_slice()).unwrap().to_string()) } + fn update_height(&mut self, output: String) -> io::Result<()> { + let (height, overflow) = common::get_height(&self.term, output); + self.height = height; + self.overflow = overflow; + Ok(()) + } + fn clear(&mut self) -> io::Result<()> { + self.term.move_cursor_up(self.overflow)?; self.term.clear_to_end_of_screen()?; self.term.clear_last_lines(self.height)?; self.height = 0; diff --git a/src/input.rs b/src/input.rs index db848e1..ff85ec9 100644 --- a/src/input.rs +++ b/src/input.rs @@ -6,7 +6,7 @@ use std::{ use console::{measure_text_width, Key, Term}; use termcolor::{Buffer, WriteColor}; -use crate::{theme, Theme}; +use crate::{common, theme, Theme}; /// Single line text input /// @@ -41,6 +41,7 @@ pub struct Input<'a> { cursor: usize, height: usize, + overflow: usize, term: Term, err: Option, } @@ -65,6 +66,7 @@ impl<'a> Input<'a> { validation: |_| Ok(()), cursor: 0, height: 0, + overflow: 0, term: Term::stderr(), err: None, } @@ -130,8 +132,6 @@ impl<'a> Input<'a> { loop { self.clear()?; let output = self.render()?; - - self.height = output.lines().count() - 1; self.term.write_all(output.as_bytes())?; self.term.flush()?; self.set_cursor()?; @@ -150,6 +150,7 @@ impl<'a> Input<'a> { self.clear_err()?; self.validate()?; if self.err.is_none() { + self.update_height(output)?; return self.handle_submit(); } } @@ -158,6 +159,8 @@ impl<'a> Input<'a> { if key != Key::Enter { self.clear_err()?; } + + self.update_height(output)?; } } @@ -320,6 +323,13 @@ impl<'a> Input<'a> { .unwrap_or(self.input.len()) } + fn update_height(&mut self, output: String) -> io::Result<()> { + let (height, overflow) = common::get_height(&self.term, output); + self.height = height; + self.overflow = overflow; + Ok(()) + } + fn set_cursor(&mut self) -> io::Result<()> { if !self.placeholder.is_empty() && self.input.is_empty() { self.term @@ -359,6 +369,7 @@ impl<'a> Input<'a> { } fn clear(&mut self) -> io::Result<()> { + self.term.move_cursor_up(self.overflow)?; self.term.clear_to_end_of_screen()?; self.term.clear_last_lines(self.height)?; self.height = 0; diff --git a/src/lib.rs b/src/lib.rs index 0215cbc..c6f85df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub use spinner::Spinner; pub use spinner::SpinnerStyle; pub use theme::Theme; +mod common; mod confirm; mod input; mod multiselect; diff --git a/src/spinner.rs b/src/spinner.rs index d370ba5..0c5239b 100644 --- a/src/spinner.rs +++ b/src/spinner.rs @@ -8,7 +8,7 @@ use console::Term; use once_cell::sync::Lazy; use termcolor::{Buffer, WriteColor}; -use crate::{theme, Theme}; +use crate::{common, theme, Theme}; /// Show a spinner /// @@ -36,6 +36,7 @@ pub struct Spinner<'a> { term: Term, frame: usize, height: usize, + overflow: usize, } impl<'a> Spinner<'a> { @@ -48,6 +49,7 @@ impl<'a> Spinner<'a> { term: Term::stderr(), frame: 0, height: 0, + overflow: 0, } } @@ -76,7 +78,6 @@ impl<'a> Spinner<'a> { loop { self.clear()?; let output = self.render()?; - self.height = output.lines().count() - 1; self.term.write_all(output.as_bytes())?; sleep(self.style.fps); if handle.is_finished() { @@ -84,6 +85,7 @@ impl<'a> Spinner<'a> { self.term.show_cursor()?; break; } + self.update_height(output)?; } Ok(()) } @@ -107,7 +109,15 @@ impl<'a> Spinner<'a> { Ok(std::str::from_utf8(out.as_slice()).unwrap().to_string()) } + fn update_height(&mut self, output: String) -> io::Result<()> { + let (height, overflow) = common::get_height(&self.term, output); + self.height = height; + self.overflow = overflow; + Ok(()) + } + fn clear(&mut self) -> io::Result<()> { + self.term.move_cursor_up(self.overflow)?; self.term.clear_to_end_of_screen()?; self.term.clear_last_lines(self.height)?; self.height = 0;