From 577a232a687bd826a0fdd4aeecef7ef10ac328aa Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 22 Mar 2023 18:04:56 +0100 Subject: [PATCH] cli: Fix multiline `patch show` description output --- radicle-cli/src/commands/patch/show.rs | 8 +++-- radicle-term/src/element.rs | 14 ++++++++ radicle-term/src/label.rs | 5 +++ radicle-term/src/lib.rs | 2 ++ radicle-term/src/textarea.rs | 46 ++++++++++++++++++++++++++ radicle-term/src/vstack.rs | 5 ++- 6 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 radicle-term/src/textarea.rs diff --git a/radicle-cli/src/commands/patch/show.rs b/radicle-cli/src/commands/patch/show.rs index 2a4a7c9c..c3c95f69 100644 --- a/radicle-cli/src/commands/patch/show.rs +++ b/radicle-cli/src/commands/patch/show.rs @@ -7,9 +7,8 @@ use radicle::cob::patch; use radicle::git; use radicle::storage::git::Repository; use radicle_term::{ - label, table::{Table, TableOptions}, - Element, Paint, VStack, + textarea, Element, Paint, VStack, }; use crate::terminal as term; @@ -71,7 +70,10 @@ pub fn run( .border(Some(term::colors::FAINT)) .child(attrs) .children(if !description.is_empty() { - vec![term::Label::blank(), label(term::format::dim(description))] + vec![ + term::Label::blank().boxed(), + textarea(term::format::dim(description)).boxed(), + ] } else { vec![] }); diff --git a/radicle-term/src/element.rs b/radicle-term/src/element.rs index 717d9d5c..3cdfda0b 100644 --- a/radicle-term/src/element.rs +++ b/radicle-term/src/element.rs @@ -43,6 +43,20 @@ pub trait Element: fmt::Debug { } } +impl<'a> Element for Box { + fn size(&self) -> Size { + self.deref().size() + } + + fn render(&self) -> Vec { + self.deref().render() + } + + fn print(&self) { + self.deref().print() + } +} + impl Element for &T { fn size(&self) -> Size { self.deref().size() diff --git a/radicle-term/src/label.rs b/radicle-term/src/label.rs index 82cb0d24..b114fb92 100644 --- a/radicle-term/src/label.rs +++ b/radicle-term/src/label.rs @@ -21,6 +21,11 @@ impl Label { pub fn space() -> Self { Self(Paint::new(" ".to_owned())) } + + /// Box the label. + pub fn boxed(self) -> Box { + Box::new(self) + } } impl Element for Label { diff --git a/radicle-term/src/lib.rs b/radicle-term/src/lib.rs index 46671a63..eff8f598 100644 --- a/radicle-term/src/lib.rs +++ b/radicle-term/src/lib.rs @@ -10,6 +10,7 @@ pub mod io; pub mod label; pub mod spinner; pub mod table; +pub mod textarea; pub mod vstack; pub use ansi::Color; @@ -22,6 +23,7 @@ pub use io::*; pub use label::{label, Label}; pub use spinner::{spinner, Spinner}; pub use table::Table; +pub use textarea::{textarea, TextArea}; pub use vstack::{VStack, VStackOptions}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] diff --git a/radicle-term/src/textarea.rs b/radicle-term/src/textarea.rs new file mode 100644 index 00000000..110df03e --- /dev/null +++ b/radicle-term/src/textarea.rs @@ -0,0 +1,46 @@ +use unicode_width::UnicodeWidthStr; + +use crate::{Element, Line, Paint, Size}; + +/// Text area. +/// +/// A block of text that can contain multiple lines. +#[derive(Debug)] +pub struct TextArea(Paint); + +impl TextArea { + /// Create a new text area. + pub fn new(content: impl Into>) -> Self { + Self(content.into()) + } + + /// Get the lines of text in this text area. + pub fn lines(&self) -> impl Iterator { + self.0.content().lines() + } + + /// Box the text area. + pub fn boxed(self) -> Box { + Box::new(self) + } +} + +impl Element for TextArea { + fn size(&self) -> Size { + let cols = self.lines().map(|l| l.width()).max().unwrap_or(0); + let rows = self.lines().count(); + + Size::new(cols, rows) + } + + fn render(&self) -> Vec { + self.lines() + .map(|l| Line::new(Paint::new(l).with_style(self.0.style))) + .collect() + } +} + +/// Create a new text area. +pub fn textarea(content: impl Into>) -> TextArea { + TextArea::new(content) +} diff --git a/radicle-term/src/vstack.rs b/radicle-term/src/vstack.rs index 2fbb12c2..da5b2454 100644 --- a/radicle-term/src/vstack.rs +++ b/radicle-term/src/vstack.rs @@ -42,7 +42,10 @@ impl<'a> VStack<'a> { } /// Add multiple elements to the stack. - pub fn children(self, children: impl IntoIterator) -> Self { + pub fn children(self, children: I) -> Self + where + I: IntoIterator>, + { let mut vstack = self; for child in children.into_iter() {