1use std::num::NonZeroU16;
4
5use anstyle::Style;
6
7use crate::line::Line;
8use crate::protocol::{Context, Measurement, Renderable};
9use crate::sanitize::{Breaks, clean, expand_tabs};
10use crate::span::Span;
11use crate::wrap::Overflow;
12
13#[derive(Clone, Debug, Eq, Hash, PartialEq)]
14struct Segment {
15 text: String,
16 style: Style,
17}
18
19#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
37pub struct Text {
38 segments: Vec<Segment>,
39 overflow: Overflow,
40}
41
42impl Text {
43 #[must_use]
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 #[must_use]
51 pub fn plain(text: impl AsRef<str>) -> Self {
52 Self::styled(text, Style::new())
53 }
54
55 #[must_use]
57 pub fn styled(text: impl AsRef<str>, style: Style) -> Self {
58 let mut created = Self::new();
59 created.push(text, style);
60 created
61 }
62
63 pub fn push(&mut self, text: impl AsRef<str>, style: Style) {
66 self.push_str(text.as_ref(), style);
67 }
68
69 fn push_str(&mut self, text: &str, style: Style) {
70 self.push_segment(clean(text, Breaks::Newline), style);
71 }
72
73 fn push_segment(&mut self, text: String, style: Style) {
76 if text.is_empty() {
77 return;
78 }
79 match self.segments.last_mut() {
80 Some(last) if last.style == style => last.text.push_str(&text),
81 Some(_) | None => self.segments.push(Segment { text, style }),
82 }
83 }
84
85 #[must_use]
88 pub fn overflow(mut self, overflow: Overflow) -> Self {
89 self.overflow = overflow;
90 self
91 }
92
93 #[must_use]
98 pub fn lines(&self, tab_width: NonZeroU16) -> Vec<Line> {
99 let mut lines: Vec<Line> = Vec::new();
100 for segment in &self.segments {
101 for (index, part) in segment.text.split('\n').enumerate() {
102 let mut line = if index == 0 {
103 lines.pop().unwrap_or_default()
104 } else {
105 Line::new()
106 };
107 append_expanded(&mut line, part, segment.style, tab_width);
108 lines.push(line);
109 }
110 }
111 lines
112 }
113
114 fn trimmed_lines(&self, context: &Context) -> Vec<Line> {
116 let mut lines = self.lines(context.tab_width());
117 for line in &mut lines {
118 line.trim_end();
119 }
120 lines
121 }
122}
123
124fn append_expanded(line: &mut Line, part: &str, style: Style, tab_width: NonZeroU16) {
131 let (head, tabbed) = part.split_at(part.find('\t').unwrap_or(part.len()));
132 line.append(head, style);
133 if !tabbed.is_empty() {
134 let column = usize::from(line.width());
135 line.append(&expand_tabs(tabbed, column, tab_width), style);
136 }
137}
138
139impl From<&str> for Text {
140 fn from(text: &str) -> Self {
141 Self::plain(text)
142 }
143}
144
145impl From<String> for Text {
146 fn from(text: String) -> Self {
147 Self::plain(text)
148 }
149}
150
151impl From<Span> for Text {
152 fn from(span: Span) -> Self {
153 let mut text = Self::new();
154 text.push_segment(span.text, span.style);
155 text
156 }
157}
158
159impl Text {
160 #[must_use]
164 pub fn measure_as(&self, context: &Context, overflow: Overflow) -> Measurement {
165 let lines = self.trimmed_lines(context);
166 let maximum = lines.iter().map(Line::width).max().unwrap_or(0);
167 let minimum = lines
168 .iter()
169 .map(|line| line.minimum_width(overflow))
170 .max()
171 .unwrap_or(0);
172 Measurement::new(minimum, maximum)
173 }
174
175 #[must_use]
179 pub fn render_as(&self, context: &Context, overflow: Overflow) -> Vec<Line> {
180 self.trimmed_lines(context)
181 .iter()
182 .flat_map(|line| line.fit(context.width(), overflow))
183 .collect()
184 }
185}
186
187impl Renderable for Text {
188 fn measure(&self, context: &Context) -> Measurement {
189 self.measure_as(context, self.overflow)
190 }
191
192 fn render(&self, context: &Context) -> Vec<Line> {
193 self.render_as(context, self.overflow)
194 }
195}