Skip to content

Commit 26af650

Browse files
authored
feat(text): add push methods for text and line (#998)
Adds the following methods to the `Text` and `Line` structs: - Text::push_line - Text::push_span - Line::push_span This allows for adding lines and spans to a text object without having to call methods on the fields directly, which is usefult for incremental construction of text objects.
1 parent 07da90a commit 26af650

File tree

3 files changed

+174
-57
lines changed

3 files changed

+174
-57
lines changed

src/text/line.rs

+47-18
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use crate::prelude::*;
4848
/// - [`Line::reset_style`] resets the style of the line.
4949
/// - [`Line::width`] returns the unicode width of the content held by this line.
5050
/// - [`Line::styled_graphemes`] returns an iterator over the graphemes held by this line.
51+
/// - [`Line::push_span`] adds a span to the line.
5152
///
5253
/// # Compatibility Notes
5354
///
@@ -451,6 +452,23 @@ impl<'a> Line<'a> {
451452
pub fn iter_mut(&mut self) -> std::slice::IterMut<Span<'a>> {
452453
self.spans.iter_mut()
453454
}
455+
456+
/// Adds a span to the line.
457+
///
458+
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
459+
/// `&str`, a `String`, or a `Span`.
460+
///
461+
/// # Examples
462+
///
463+
/// ```rust
464+
/// # use ratatui::prelude::*;
465+
/// let mut line = Line::from("Hello, ");
466+
/// line.push_span(Span::raw("world!"));
467+
/// line.push_span(" How are you?");
468+
/// ```
469+
pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
470+
self.spans.push(span.into());
471+
}
454472
}
455473

456474
impl<'a> IntoIterator for Line<'a> {
@@ -830,6 +848,35 @@ mod tests {
830848
assert_eq!(format!("{line_from_styled_span}"), "Hello, world!");
831849
}
832850

851+
#[test]
852+
fn left_aligned() {
853+
let line = Line::from("Hello, world!").left_aligned();
854+
assert_eq!(line.alignment, Some(Alignment::Left));
855+
}
856+
857+
#[test]
858+
fn centered() {
859+
let line = Line::from("Hello, world!").centered();
860+
assert_eq!(line.alignment, Some(Alignment::Center));
861+
}
862+
863+
#[test]
864+
fn right_aligned() {
865+
let line = Line::from("Hello, world!").right_aligned();
866+
assert_eq!(line.alignment, Some(Alignment::Right));
867+
}
868+
869+
#[test]
870+
pub fn push_span() {
871+
let mut line = Line::from("A");
872+
line.push_span(Span::raw("B"));
873+
line.push_span("C");
874+
assert_eq!(
875+
line.spans,
876+
vec![Span::raw("A"), Span::raw("B"), Span::raw("C")]
877+
);
878+
}
879+
833880
mod widget {
834881
use super::*;
835882
use crate::assert_buffer_eq;
@@ -908,24 +955,6 @@ mod tests {
908955
}
909956
}
910957

911-
#[test]
912-
fn left_aligned() {
913-
let line = Line::from("Hello, world!").left_aligned();
914-
assert_eq!(line.alignment, Some(Alignment::Left));
915-
}
916-
917-
#[test]
918-
fn centered() {
919-
let line = Line::from("Hello, world!").centered();
920-
assert_eq!(line.alignment, Some(Alignment::Center));
921-
}
922-
923-
#[test]
924-
fn right_aligned() {
925-
let line = Line::from("Hello, world!").right_aligned();
926-
assert_eq!(line.alignment, Some(Alignment::Right));
927-
}
928-
929958
mod iterators {
930959
use super::*;
931960

src/text/span.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,27 @@ mod tests {
540540
assert_eq!(format!("{stylized_span}"), "stylized test content");
541541
}
542542

543+
#[test]
544+
fn left_aligned() {
545+
let span = Span::styled("Test Content", Style::new().green().italic());
546+
let line = span.into_left_aligned_line();
547+
assert_eq!(line.alignment, Some(Alignment::Left));
548+
}
549+
550+
#[test]
551+
fn centered() {
552+
let span = Span::styled("Test Content", Style::new().green().italic());
553+
let line = span.into_centered_line();
554+
assert_eq!(line.alignment, Some(Alignment::Center));
555+
}
556+
557+
#[test]
558+
fn right_aligned() {
559+
let span = Span::styled("Test Content", Style::new().green().italic());
560+
let line = span.into_right_aligned_line();
561+
assert_eq!(line.alignment, Some(Alignment::Right));
562+
}
563+
543564
mod widget {
544565
use rstest::rstest;
545566

@@ -649,25 +670,4 @@ mod tests {
649670
assert_buffer_eq!(buf, expected);
650671
}
651672
}
652-
653-
#[test]
654-
fn left_aligned() {
655-
let span = Span::styled("Test Content", Style::new().green().italic());
656-
let line = span.into_left_aligned_line();
657-
assert_eq!(line.alignment, Some(Alignment::Left));
658-
}
659-
660-
#[test]
661-
fn centered() {
662-
let span = Span::styled("Test Content", Style::new().green().italic());
663-
let line = span.into_centered_line();
664-
assert_eq!(line.alignment, Some(Alignment::Center));
665-
}
666-
667-
#[test]
668-
fn right_aligned() {
669-
let span = Span::styled("Test Content", Style::new().green().italic());
670-
let line = span.into_right_aligned_line();
671-
assert_eq!(line.alignment, Some(Alignment::Right));
672-
}
673673
}

src/text/text.rs

+106-18
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ use crate::prelude::*;
5050
/// - [`Text::height`] returns the height.
5151
/// - [`Text::patch_style`] patches the style of this `Text`, adding modifiers from the given style.
5252
/// - [`Text::reset_style`] resets the style of the `Text`.
53+
/// - [`Text::push_line`] adds a line to the text.
54+
/// - [`Text::push_span`] adds a span to the last line of the text.
5355
///
5456
/// # Examples
5557
///
@@ -441,6 +443,46 @@ impl<'a> Text<'a> {
441443
pub fn iter_mut(&mut self) -> std::slice::IterMut<Line<'a>> {
442444
self.lines.iter_mut()
443445
}
446+
447+
/// Adds a line to the text.
448+
///
449+
/// `line` can be any type that can be converted into a `Line`. For example, you can pass a
450+
/// `&str`, a `String`, a `Span`, or a `Line`.
451+
///
452+
/// # Examples
453+
///
454+
/// ```rust
455+
/// # use ratatui::prelude::*;
456+
/// let mut text = Text::from("Hello, world!");
457+
/// text.push_line(Line::from("How are you?"));
458+
/// text.push_line(Span::from("How are you?"));
459+
/// text.push_line("How are you?");
460+
/// ```
461+
pub fn push_line<T: Into<Line<'a>>>(&mut self, line: T) {
462+
self.lines.push(line.into());
463+
}
464+
465+
/// Adds a span to the last line of the text.
466+
///
467+
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
468+
/// `&str`, a `String`, or a `Span`.
469+
///
470+
/// # Examples
471+
///
472+
/// ```rust
473+
/// # use ratatui::prelude::*;
474+
/// let mut text = Text::from("Hello, world!");
475+
/// text.push_span(Span::from("How are you?"));
476+
/// text.push_span("How are you?");
477+
/// ```
478+
pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
479+
let span = span.into();
480+
if let Some(last) = self.lines.last_mut() {
481+
last.push_span(span);
482+
} else {
483+
self.lines.push(Line::from(span));
484+
}
485+
}
444486
}
445487

446488
impl<'a> IntoIterator for Text<'a> {
@@ -854,6 +896,70 @@ mod tests {
854896
assert_eq!(Text::default().italic().style, Modifier::ITALIC.into());
855897
}
856898

899+
#[test]
900+
fn left_aligned() {
901+
let text = Text::from("Hello, world!").left_aligned();
902+
assert_eq!(text.alignment, Some(Alignment::Left));
903+
}
904+
905+
#[test]
906+
fn centered() {
907+
let text = Text::from("Hello, world!").centered();
908+
assert_eq!(text.alignment, Some(Alignment::Center));
909+
}
910+
911+
#[test]
912+
fn right_aligned() {
913+
let text = Text::from("Hello, world!").right_aligned();
914+
assert_eq!(text.alignment, Some(Alignment::Right));
915+
}
916+
917+
#[test]
918+
fn push_line() {
919+
let mut text = Text::from("A");
920+
text.push_line(Line::from("B"));
921+
text.push_line(Span::from("C"));
922+
text.push_line("D");
923+
assert_eq!(
924+
text.lines,
925+
vec![
926+
Line::raw("A"),
927+
Line::raw("B"),
928+
Line::raw("C"),
929+
Line::raw("D")
930+
]
931+
);
932+
}
933+
934+
#[test]
935+
fn push_line_empty() {
936+
let mut text = Text::default();
937+
text.push_line(Line::from("Hello, world!"));
938+
assert_eq!(text.lines, vec![Line::from("Hello, world!")]);
939+
}
940+
941+
#[test]
942+
fn push_span() {
943+
let mut text = Text::from("A");
944+
text.push_span(Span::raw("B"));
945+
text.push_span("C");
946+
assert_eq!(
947+
text.lines,
948+
vec![Line::from(vec![
949+
Span::raw("A"),
950+
Span::raw("B"),
951+
Span::raw("C")
952+
])],
953+
);
954+
}
955+
956+
#[test]
957+
fn push_span_empty() {
958+
let mut text = Text::default();
959+
text.push_span(Span::raw("Hello, world!"));
960+
assert_eq!(text.lines, vec![Line::from(Span::raw("Hello, world!"))],);
961+
}
962+
857963
mod widget {
858964
use super::*;
859965
use crate::assert_buffer_eq;
@@ -958,24 +1064,6 @@ mod tests {
9581064
}
9591065
}
9601066

961-
#[test]
962-
fn left_aligned() {
963-
let text = Text::from("Hello, world!").left_aligned();
964-
assert_eq!(text.alignment, Some(Alignment::Left));
965-
}
966-
967-
#[test]
968-
fn centered() {
969-
let text = Text::from("Hello, world!").centered();
970-
assert_eq!(text.alignment, Some(Alignment::Center));
971-
}
972-
973-
#[test]
974-
fn right_aligned() {
975-
let text = Text::from("Hello, world!").right_aligned();
976-
assert_eq!(text.alignment, Some(Alignment::Right));
977-
}
978-
9791067
mod iterators {
9801068
use super::*;
9811069

0 commit comments

Comments
 (0)