1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0
#![forbid(unsafe_code)]
use tui::{
style::Style,
text::{Span, Spans},
};
/// A `TextBuilder` is used to build up a paragraph, where some parts of it may need to have
/// different styling, and where this styling may not conform to line boundaries.
#[derive(Debug, Clone, Default)]
pub struct TextBuilder<'a> {
// A vec of "lines" each line is a vector of spans.
// We use Vec<Span> here instead of Spans since otherwise we wouldn't be able to join lines in
// the `add` function.
chunks: Vec<Vec<Span<'a>>>,
}
impl<'a> TextBuilder<'a> {
/// Create a new text builder
pub fn new() -> Self {
Self { chunks: Vec::new() }
}
/// Add `text` with the given `style`ing to the text builder. This functions tracks newlines in
/// the text already recorded (in the `chunks` field), and will splice lines between the
/// previous text and the new `text` being added. It respects the `style` of both the old text
/// and the newly added text.
pub fn add(&mut self, text: String, style: Style) {
let chunk = |string: String| {
string
.split('\n')
.map(|x| x.to_string())
.map(|x| vec![Span::styled(x, style)])
.collect::<Vec<Vec<Span>>>()
};
let last_chunk_ends_with_nl = self
.chunks
.last()
.map(|last_span| {
last_span
.last()
.map(|last_span| last_span.content.ends_with('\n'))
.unwrap_or(false)
})
.unwrap_or(true);
if !last_chunk_ends_with_nl {
let mut iter = text.splitn(2, '\n');
iter.next().into_iter().for_each(|line_continuation| {
self.chunks
.last_mut()
.unwrap()
.push(Span::styled(line_continuation.to_string(), style));
});
iter.next().into_iter().for_each(|remainder| {
self.chunks.extend(chunk(remainder.to_string()).into_iter());
});
} else {
self.chunks.extend(chunk(text))
}
}
/// Return back the final Spans, each `Spans` represents a line in the paragraph.
pub fn finish(self) -> Vec<Spans<'a>> {
self.chunks.into_iter().map(Spans::from).collect()
}
}