//! Words into cells. //! //! A terminal wraps on words and counts rows, and both halves have to agree or //! a node draws over the one under it. So the wrap is written once here and //! both [`height`] and [`draw`] read it, rather than each having its own idea //! of how many rows a paragraph takes. //! //! Nothing below is about a described screen. //! Flow layout is the shape every terminal consumer in the tree ends up with — //! ask for a height at a width, then draw into the rect you were given — and it //! needs a wrap that answers both questions the same way. ratatui's own //! `Paragraph` wraps but will not tell you how many rows it took, which is the //! half a flow layout cannot do without. //! //! Width is counted in `char`s. That is wrong for a terminal in the general //! case -- a CJK glyph occupies two cells and a combining mark none -- and it //! is deliberately not fixed here: the fix is a `unicode-width` dependency, and //! taking one before anything in the tree has non-ASCII content to draw is //! paying for a problem nobody has yet. Filed rather than hidden. use ratatui::buffer::Buffer; use ratatui::layout::Rect; use ratatui::style::Style; use ratatui::text::{Line, Span}; /// Break `spans` into lines no wider than `width`, keeping each word under the /// style it arrived with. /// /// Breaks on whitespace, and breaks inside a word only when the word cannot fit /// on a line of its own. A word longer than the whole width is the case that /// has no good answer; cutting it is the least bad one, because the alternative /// is a line wider than the region and a buffer that swallows the overflow /// silently. /// /// The one wrap in this crate. [`wrap`] is this with a single style over the /// whole string, rather than a second implementation that would be free to /// disagree with it about how many rows a paragraph takes -- and a disagreement /// there is a node drawing over the one under it. pub fn wrap_spans(spans: &[Span<'_>], width: u16) -> Vec> { if width == 0 { return Vec::new(); } let width = width as usize; let mut lines: Vec>> = Vec::new(); let mut line: Vec> = Vec::new(); let mut column = 0usize; // The style of the whitespace last passed over, held until a word turns up // to need a separator before it. Kept rather than taken from the word, // because the space between `*lean*` and `~~gone~~` belongs to the plain // run that held it: a strikethrough that starts one cell early is drawn // through a space the author never struck. let mut separator: Option