@akiojin/gwt 9.0.4 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +106 -146
- package/README.md +103 -143
- package/bin/gwt.cjs +1 -1
- package/package.json +5 -5
- package/rustfmt.toml +0 -2
- package/scripts/check-release-flow.sh +2 -8
- package/scripts/postinstall.js +17 -7
- package/scripts/run-local-backend-tests-on-commit.sh +6 -12
- package/scripts/test-all.sh +1 -5
- package/scripts/check-e2e-coverage-threshold.mjs +0 -238
- package/scripts/run-local-e2e-coverage-on-commit.sh +0 -69
- package/scripts/run-local-e2e-on-commit.sh +0 -60
- package/scripts/verify-ci-node-toolchain.sh +0 -76
- package/scripts/voice-eval.sh +0 -48
- package/vendor/ratatui-core/src/backend/test.rs +0 -1077
- package/vendor/ratatui-core/src/backend.rs +0 -405
- package/vendor/ratatui-core/src/buffer/assert.rs +0 -71
- package/vendor/ratatui-core/src/buffer/buffer.rs +0 -1388
- package/vendor/ratatui-core/src/buffer/cell.rs +0 -377
- package/vendor/ratatui-core/src/buffer.rs +0 -9
- package/vendor/ratatui-core/src/layout/alignment.rs +0 -89
- package/vendor/ratatui-core/src/layout/constraint.rs +0 -526
- package/vendor/ratatui-core/src/layout/direction.rs +0 -63
- package/vendor/ratatui-core/src/layout/flex.rs +0 -212
- package/vendor/ratatui-core/src/layout/layout.rs +0 -2838
- package/vendor/ratatui-core/src/layout/margin.rs +0 -79
- package/vendor/ratatui-core/src/layout/offset.rs +0 -66
- package/vendor/ratatui-core/src/layout/position.rs +0 -253
- package/vendor/ratatui-core/src/layout/rect/iter.rs +0 -356
- package/vendor/ratatui-core/src/layout/rect/ops.rs +0 -136
- package/vendor/ratatui-core/src/layout/rect.rs +0 -1114
- package/vendor/ratatui-core/src/layout/size.rs +0 -147
- package/vendor/ratatui-core/src/layout.rs +0 -333
- package/vendor/ratatui-core/src/lib.rs +0 -82
- package/vendor/ratatui-core/src/style/anstyle.rs +0 -348
- package/vendor/ratatui-core/src/style/color.rs +0 -788
- package/vendor/ratatui-core/src/style/palette/material.rs +0 -608
- package/vendor/ratatui-core/src/style/palette/tailwind.rs +0 -653
- package/vendor/ratatui-core/src/style/palette.rs +0 -6
- package/vendor/ratatui-core/src/style/palette_conversion.rs +0 -82
- package/vendor/ratatui-core/src/style/stylize.rs +0 -668
- package/vendor/ratatui-core/src/style.rs +0 -1069
- package/vendor/ratatui-core/src/symbols/bar.rs +0 -51
- package/vendor/ratatui-core/src/symbols/block.rs +0 -51
- package/vendor/ratatui-core/src/symbols/border.rs +0 -709
- package/vendor/ratatui-core/src/symbols/braille.rs +0 -21
- package/vendor/ratatui-core/src/symbols/half_block.rs +0 -3
- package/vendor/ratatui-core/src/symbols/line.rs +0 -259
- package/vendor/ratatui-core/src/symbols/marker.rs +0 -82
- package/vendor/ratatui-core/src/symbols/merge.rs +0 -748
- package/vendor/ratatui-core/src/symbols/pixel.rs +0 -30
- package/vendor/ratatui-core/src/symbols/scrollbar.rs +0 -46
- package/vendor/ratatui-core/src/symbols/shade.rs +0 -5
- package/vendor/ratatui-core/src/symbols.rs +0 -15
- package/vendor/ratatui-core/src/terminal/frame.rs +0 -192
- package/vendor/ratatui-core/src/terminal/terminal.rs +0 -926
- package/vendor/ratatui-core/src/terminal/viewport.rs +0 -58
- package/vendor/ratatui-core/src/terminal.rs +0 -40
- package/vendor/ratatui-core/src/text/grapheme.rs +0 -84
- package/vendor/ratatui-core/src/text/line.rs +0 -1678
- package/vendor/ratatui-core/src/text/masked.rs +0 -149
- package/vendor/ratatui-core/src/text/span.rs +0 -904
- package/vendor/ratatui-core/src/text/text.rs +0 -1434
- package/vendor/ratatui-core/src/text.rs +0 -64
- package/vendor/ratatui-core/src/widgets/stateful_widget.rs +0 -193
- package/vendor/ratatui-core/src/widgets/widget.rs +0 -174
- package/vendor/ratatui-core/src/widgets.rs +0 -9
|
@@ -1,1678 +0,0 @@
|
|
|
1
|
-
#![deny(missing_docs)]
|
|
2
|
-
#![warn(clippy::pedantic, clippy::nursery, clippy::arithmetic_side_effects)]
|
|
3
|
-
use alloc::borrow::Cow;
|
|
4
|
-
use alloc::string::{String, ToString};
|
|
5
|
-
use alloc::vec;
|
|
6
|
-
use alloc::vec::Vec;
|
|
7
|
-
use core::fmt;
|
|
8
|
-
|
|
9
|
-
use unicode_truncate::UnicodeTruncateStr;
|
|
10
|
-
use unicode_width::UnicodeWidthStr;
|
|
11
|
-
|
|
12
|
-
use crate::buffer::Buffer;
|
|
13
|
-
use crate::layout::{Alignment, Rect};
|
|
14
|
-
use crate::style::{Style, Styled};
|
|
15
|
-
use crate::text::{Span, StyledGrapheme, Text};
|
|
16
|
-
use crate::widgets::Widget;
|
|
17
|
-
|
|
18
|
-
/// A line of text, consisting of one or more [`Span`]s.
|
|
19
|
-
///
|
|
20
|
-
/// [`Line`]s are used wherever text is displayed in the terminal and represent a single line of
|
|
21
|
-
/// text. When a [`Line`] is rendered, it is rendered as a single line of text, with each [`Span`]
|
|
22
|
-
/// being rendered in order (left to right).
|
|
23
|
-
///
|
|
24
|
-
/// Any newlines in the content are removed when creating a [`Line`] using the constructor or
|
|
25
|
-
/// conversion methods.
|
|
26
|
-
///
|
|
27
|
-
/// # Constructor Methods
|
|
28
|
-
///
|
|
29
|
-
/// - [`Line::default`] creates a line with empty content and the default style.
|
|
30
|
-
/// - [`Line::raw`] creates a line with the given content and the default style.
|
|
31
|
-
/// - [`Line::styled`] creates a line with the given content and style.
|
|
32
|
-
///
|
|
33
|
-
/// # Conversion Methods
|
|
34
|
-
///
|
|
35
|
-
/// - [`Line::from`] creates a `Line` from a [`String`].
|
|
36
|
-
/// - [`Line::from`] creates a `Line` from a [`&str`].
|
|
37
|
-
/// - [`Line::from`] creates a `Line` from a [`Vec`] of [`Span`]s.
|
|
38
|
-
/// - [`Line::from`] creates a `Line` from single [`Span`].
|
|
39
|
-
/// - [`String::from`] converts a line into a [`String`].
|
|
40
|
-
/// - [`Line::from_iter`] creates a line from an iterator of items that are convertible to [`Span`].
|
|
41
|
-
///
|
|
42
|
-
/// # Setter Methods
|
|
43
|
-
///
|
|
44
|
-
/// These methods are fluent setters. They return a `Line` with the property set.
|
|
45
|
-
///
|
|
46
|
-
/// - [`Line::spans`] sets the content of the line.
|
|
47
|
-
/// - [`Line::style`] sets the style of the line.
|
|
48
|
-
/// - [`Line::alignment`] sets the alignment of the line.
|
|
49
|
-
/// - [`Line::left_aligned`] sets the alignment of the line to [`Alignment::Left`].
|
|
50
|
-
/// - [`Line::centered`] sets the alignment of the line to [`Alignment::Center`].
|
|
51
|
-
/// - [`Line::right_aligned`] sets the alignment of the line to [`Alignment::Right`].
|
|
52
|
-
///
|
|
53
|
-
/// # Iteration Methods
|
|
54
|
-
///
|
|
55
|
-
/// - [`Line::iter`] returns an iterator over the spans of this line.
|
|
56
|
-
/// - [`Line::iter_mut`] returns a mutable iterator over the spans of this line.
|
|
57
|
-
/// - [`Line::into_iter`] returns an iterator over the spans of this line.
|
|
58
|
-
///
|
|
59
|
-
/// # Other Methods
|
|
60
|
-
///
|
|
61
|
-
/// - [`Line::patch_style`] patches the style of the line, adding modifiers from the given style.
|
|
62
|
-
/// - [`Line::reset_style`] resets the style of the line.
|
|
63
|
-
/// - [`Line::width`] returns the unicode width of the content held by this line.
|
|
64
|
-
/// - [`Line::styled_graphemes`] returns an iterator over the graphemes held by this line.
|
|
65
|
-
/// - [`Line::push_span`] adds a span to the line.
|
|
66
|
-
///
|
|
67
|
-
/// # Compatibility Notes
|
|
68
|
-
///
|
|
69
|
-
/// Before v0.26.0, [`Line`] did not have a `style` field and instead relied on only the styles that
|
|
70
|
-
/// were set on each [`Span`] contained in the `spans` field. The [`Line::patch_style`] method was
|
|
71
|
-
/// the only way to set the overall style for individual lines. For this reason, this field may not
|
|
72
|
-
/// be supported yet by all widgets (outside of the `ratatui` crate itself).
|
|
73
|
-
///
|
|
74
|
-
/// # Examples
|
|
75
|
-
///
|
|
76
|
-
/// ## Creating Lines
|
|
77
|
-
/// [`Line`]s can be created from [`Span`]s, [`String`]s, and [`&str`]s. They can be styled with a
|
|
78
|
-
/// [`Style`].
|
|
79
|
-
///
|
|
80
|
-
/// ```rust
|
|
81
|
-
/// use ratatui_core::style::{Color, Modifier, Style, Stylize};
|
|
82
|
-
/// use ratatui_core::text::{Line, Span};
|
|
83
|
-
///
|
|
84
|
-
/// let style = Style::new().yellow();
|
|
85
|
-
/// let line = Line::raw("Hello, world!").style(style);
|
|
86
|
-
/// let line = Line::styled("Hello, world!", style);
|
|
87
|
-
/// let line = Line::styled("Hello, world!", (Color::Yellow, Modifier::BOLD));
|
|
88
|
-
///
|
|
89
|
-
/// let line = Line::from("Hello, world!");
|
|
90
|
-
/// let line = Line::from(String::from("Hello, world!"));
|
|
91
|
-
/// let line = Line::from(vec![
|
|
92
|
-
/// Span::styled("Hello", Style::new().blue()),
|
|
93
|
-
/// Span::raw(" world!"),
|
|
94
|
-
/// ]);
|
|
95
|
-
/// ```
|
|
96
|
-
///
|
|
97
|
-
/// ## Styling Lines
|
|
98
|
-
///
|
|
99
|
-
/// The line's [`Style`] is used by the rendering widget to determine how to style the line. Each
|
|
100
|
-
/// [`Span`] in the line will be styled with the [`Style`] of the line, and then with its own
|
|
101
|
-
/// [`Style`]. If the line is longer than the available space, the style is applied to the entire
|
|
102
|
-
/// line, and the line is truncated. `Line` also implements [`Styled`] which means you can use the
|
|
103
|
-
/// methods of the [`Stylize`] trait.
|
|
104
|
-
///
|
|
105
|
-
/// ```rust
|
|
106
|
-
/// use ratatui_core::style::{Color, Modifier, Style, Stylize};
|
|
107
|
-
/// use ratatui_core::text::Line;
|
|
108
|
-
///
|
|
109
|
-
/// let line = Line::from("Hello world!").style(Style::new().yellow().italic());
|
|
110
|
-
/// let line = Line::from("Hello world!").style(Color::Yellow);
|
|
111
|
-
/// let line = Line::from("Hello world!").style((Color::Yellow, Color::Black));
|
|
112
|
-
/// let line = Line::from("Hello world!").style((Color::Yellow, Modifier::ITALIC));
|
|
113
|
-
/// let line = Line::from("Hello world!").yellow().italic();
|
|
114
|
-
/// ```
|
|
115
|
-
///
|
|
116
|
-
/// ## Aligning Lines
|
|
117
|
-
///
|
|
118
|
-
/// The line's [`Alignment`] is used by the rendering widget to determine how to align the line
|
|
119
|
-
/// within the available space. If the line is longer than the available space, the alignment is
|
|
120
|
-
/// ignored and the line is truncated.
|
|
121
|
-
///
|
|
122
|
-
/// ```rust
|
|
123
|
-
/// use ratatui_core::layout::Alignment;
|
|
124
|
-
/// use ratatui_core::text::Line;
|
|
125
|
-
///
|
|
126
|
-
/// let line = Line::from("Hello world!").alignment(Alignment::Right);
|
|
127
|
-
/// let line = Line::from("Hello world!").centered();
|
|
128
|
-
/// let line = Line::from("Hello world!").left_aligned();
|
|
129
|
-
/// let line = Line::from("Hello world!").right_aligned();
|
|
130
|
-
/// ```
|
|
131
|
-
///
|
|
132
|
-
/// ## Rendering Lines
|
|
133
|
-
///
|
|
134
|
-
/// `Line` implements the [`Widget`] trait, which means it can be rendered to a [`Buffer`].
|
|
135
|
-
///
|
|
136
|
-
/// ```rust
|
|
137
|
-
/// use ratatui_core::buffer::Buffer;
|
|
138
|
-
/// use ratatui_core::layout::Rect;
|
|
139
|
-
/// use ratatui_core::style::{Style, Stylize};
|
|
140
|
-
/// use ratatui_core::text::Line;
|
|
141
|
-
/// use ratatui_core::widgets::Widget;
|
|
142
|
-
///
|
|
143
|
-
/// # fn render(area: Rect, buf: &mut Buffer) {
|
|
144
|
-
/// // in another widget's render method
|
|
145
|
-
/// let line = Line::from("Hello world!").style(Style::new().yellow().italic());
|
|
146
|
-
/// line.render(area, buf);
|
|
147
|
-
/// # }
|
|
148
|
-
/// ```
|
|
149
|
-
///
|
|
150
|
-
/// Or you can use the `render_widget` method on the `Frame` in a `Terminal::draw` closure.
|
|
151
|
-
///
|
|
152
|
-
/// ```rust,ignore
|
|
153
|
-
/// # use ratatui::{Frame, layout::Rect, text::Line};
|
|
154
|
-
/// # fn draw(frame: &mut Frame, area: Rect) {
|
|
155
|
-
/// let line = Line::from("Hello world!");
|
|
156
|
-
/// frame.render_widget(line, area);
|
|
157
|
-
/// # }
|
|
158
|
-
/// ```
|
|
159
|
-
/// ## Rendering Lines with a Paragraph widget
|
|
160
|
-
///
|
|
161
|
-
/// Usually apps will use the `Paragraph` widget instead of rendering a [`Line`] directly as it
|
|
162
|
-
/// provides more functionality.
|
|
163
|
-
///
|
|
164
|
-
/// ```rust,ignore
|
|
165
|
-
/// use ratatui::{
|
|
166
|
-
/// buffer::Buffer,
|
|
167
|
-
/// layout::Rect,
|
|
168
|
-
/// style::Stylize,
|
|
169
|
-
/// text::Line,
|
|
170
|
-
/// widgets::{Paragraph, Widget, Wrap},
|
|
171
|
-
/// };
|
|
172
|
-
///
|
|
173
|
-
/// # fn render(area: Rect, buf: &mut Buffer) {
|
|
174
|
-
/// let line = Line::from("Hello world!").yellow().italic();
|
|
175
|
-
/// Paragraph::new(line)
|
|
176
|
-
/// .wrap(Wrap { trim: true })
|
|
177
|
-
/// .render(area, buf);
|
|
178
|
-
/// # }
|
|
179
|
-
/// ```
|
|
180
|
-
///
|
|
181
|
-
/// [`Stylize`]: crate::style::Stylize
|
|
182
|
-
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
|
183
|
-
pub struct Line<'a> {
|
|
184
|
-
/// The style of this line of text.
|
|
185
|
-
pub style: Style,
|
|
186
|
-
|
|
187
|
-
/// The alignment of this line of text.
|
|
188
|
-
pub alignment: Option<Alignment>,
|
|
189
|
-
|
|
190
|
-
/// The spans that make up this line of text.
|
|
191
|
-
pub spans: Vec<Span<'a>>,
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
impl fmt::Debug for Line<'_> {
|
|
195
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
196
|
-
if self.spans.is_empty() {
|
|
197
|
-
f.write_str("Line::default()")?;
|
|
198
|
-
} else if self.spans.len() == 1 && self.spans[0].style == Style::default() {
|
|
199
|
-
f.write_str(r#"Line::from(""#)?;
|
|
200
|
-
f.write_str(&self.spans[0].content)?;
|
|
201
|
-
f.write_str(r#"")"#)?;
|
|
202
|
-
} else if self.spans.len() == 1 {
|
|
203
|
-
f.write_str("Line::from(")?;
|
|
204
|
-
self.spans[0].fmt(f)?;
|
|
205
|
-
f.write_str(")")?;
|
|
206
|
-
} else {
|
|
207
|
-
f.write_str("Line::from_iter(")?;
|
|
208
|
-
f.debug_list().entries(&self.spans).finish()?;
|
|
209
|
-
f.write_str(")")?;
|
|
210
|
-
}
|
|
211
|
-
self.style.fmt_stylize(f)?;
|
|
212
|
-
match self.alignment {
|
|
213
|
-
Some(Alignment::Left) => write!(f, ".left_aligned()"),
|
|
214
|
-
Some(Alignment::Center) => write!(f, ".centered()"),
|
|
215
|
-
Some(Alignment::Right) => write!(f, ".right_aligned()"),
|
|
216
|
-
None => Ok(()),
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
fn cow_to_spans<'a>(content: impl Into<Cow<'a, str>>) -> Vec<Span<'a>> {
|
|
222
|
-
match content.into() {
|
|
223
|
-
Cow::Borrowed(s) => s.lines().map(Span::raw).collect(),
|
|
224
|
-
Cow::Owned(s) => s.lines().map(|v| Span::raw(v.to_string())).collect(),
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
impl<'a> Line<'a> {
|
|
229
|
-
/// Create a line with the default style.
|
|
230
|
-
///
|
|
231
|
-
/// `content` can be any type that is convertible to [`Cow<str>`] (e.g. [`&str`], [`String`],
|
|
232
|
-
/// [`Cow<str>`], or your own type that implements [`Into<Cow<str>>`]).
|
|
233
|
-
///
|
|
234
|
-
/// A [`Line`] can specify a [`Style`], which will be applied before the style of each [`Span`]
|
|
235
|
-
/// in the line.
|
|
236
|
-
///
|
|
237
|
-
/// Any newlines in the content are removed.
|
|
238
|
-
///
|
|
239
|
-
/// # Examples
|
|
240
|
-
///
|
|
241
|
-
/// ```rust
|
|
242
|
-
/// use std::borrow::Cow;
|
|
243
|
-
///
|
|
244
|
-
/// use ratatui_core::text::Line;
|
|
245
|
-
///
|
|
246
|
-
/// Line::raw("test content");
|
|
247
|
-
/// Line::raw(String::from("test content"));
|
|
248
|
-
/// Line::raw(Cow::from("test content"));
|
|
249
|
-
/// ```
|
|
250
|
-
pub fn raw<T>(content: T) -> Self
|
|
251
|
-
where
|
|
252
|
-
T: Into<Cow<'a, str>>,
|
|
253
|
-
{
|
|
254
|
-
Self {
|
|
255
|
-
spans: cow_to_spans(content),
|
|
256
|
-
..Default::default()
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/// Create a line with the given style.
|
|
261
|
-
///
|
|
262
|
-
/// `content` can be any type that is convertible to [`Cow<str>`] (e.g. [`&str`], [`String`],
|
|
263
|
-
/// [`Cow<str>`], or your own type that implements [`Into<Cow<str>>`]).
|
|
264
|
-
///
|
|
265
|
-
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
|
|
266
|
-
/// your own type that implements [`Into<Style>`]).
|
|
267
|
-
///
|
|
268
|
-
/// # Examples
|
|
269
|
-
///
|
|
270
|
-
/// Any newlines in the content are removed.
|
|
271
|
-
///
|
|
272
|
-
/// ```rust
|
|
273
|
-
/// use std::borrow::Cow;
|
|
274
|
-
///
|
|
275
|
-
/// use ratatui_core::style::{Style, Stylize};
|
|
276
|
-
/// use ratatui_core::text::Line;
|
|
277
|
-
///
|
|
278
|
-
/// let style = Style::new().yellow().italic();
|
|
279
|
-
/// Line::styled("My text", style);
|
|
280
|
-
/// Line::styled(String::from("My text"), style);
|
|
281
|
-
/// Line::styled(Cow::from("test content"), style);
|
|
282
|
-
/// ```
|
|
283
|
-
///
|
|
284
|
-
/// [`Color`]: crate::style::Color
|
|
285
|
-
pub fn styled<T, S>(content: T, style: S) -> Self
|
|
286
|
-
where
|
|
287
|
-
T: Into<Cow<'a, str>>,
|
|
288
|
-
S: Into<Style>,
|
|
289
|
-
{
|
|
290
|
-
Self {
|
|
291
|
-
spans: cow_to_spans(content),
|
|
292
|
-
style: style.into(),
|
|
293
|
-
..Default::default()
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/// Sets the spans of this line of text.
|
|
298
|
-
///
|
|
299
|
-
/// `spans` accepts any iterator that yields items that are convertible to [`Span`] (e.g.
|
|
300
|
-
/// [`&str`], [`String`], [`Span`], or your own type that implements [`Into<Span>`]).
|
|
301
|
-
///
|
|
302
|
-
/// # Examples
|
|
303
|
-
///
|
|
304
|
-
/// ```rust
|
|
305
|
-
/// use ratatui_core::style::Stylize;
|
|
306
|
-
/// use ratatui_core::text::Line;
|
|
307
|
-
///
|
|
308
|
-
/// let line = Line::default().spans(vec!["Hello".blue(), " world!".green()]);
|
|
309
|
-
/// let line = Line::default().spans([1, 2, 3].iter().map(|i| format!("Item {}", i)));
|
|
310
|
-
/// ```
|
|
311
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
312
|
-
pub fn spans<I>(mut self, spans: I) -> Self
|
|
313
|
-
where
|
|
314
|
-
I: IntoIterator,
|
|
315
|
-
I::Item: Into<Span<'a>>,
|
|
316
|
-
{
|
|
317
|
-
self.spans = spans.into_iter().map(Into::into).collect();
|
|
318
|
-
self
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/// Sets the style of this line of text.
|
|
322
|
-
///
|
|
323
|
-
/// Defaults to [`Style::default()`].
|
|
324
|
-
///
|
|
325
|
-
/// Note: This field was added in v0.26.0. Prior to that, the style of a line was determined
|
|
326
|
-
/// only by the style of each [`Span`] contained in the line. For this reason, this field may
|
|
327
|
-
/// not be supported by all widgets (outside of the `ratatui` crate itself).
|
|
328
|
-
///
|
|
329
|
-
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
|
|
330
|
-
/// your own type that implements [`Into<Style>`]).
|
|
331
|
-
///
|
|
332
|
-
/// # Examples
|
|
333
|
-
/// ```rust
|
|
334
|
-
/// use ratatui_core::style::{Style, Stylize};
|
|
335
|
-
/// use ratatui_core::text::Line;
|
|
336
|
-
///
|
|
337
|
-
/// let mut line = Line::from("foo").style(Style::new().red());
|
|
338
|
-
/// ```
|
|
339
|
-
///
|
|
340
|
-
/// [`Color`]: crate::style::Color
|
|
341
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
342
|
-
pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
|
|
343
|
-
self.style = style.into();
|
|
344
|
-
self
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/// Sets the target alignment for this line of text.
|
|
348
|
-
///
|
|
349
|
-
/// Defaults to: [`None`], meaning the alignment is determined by the rendering widget.
|
|
350
|
-
/// Setting the alignment of a Line generally overrides the alignment of its
|
|
351
|
-
/// parent Text or Widget.
|
|
352
|
-
///
|
|
353
|
-
/// # Examples
|
|
354
|
-
///
|
|
355
|
-
/// ```rust
|
|
356
|
-
/// use ratatui_core::layout::Alignment;
|
|
357
|
-
/// use ratatui_core::text::Line;
|
|
358
|
-
///
|
|
359
|
-
/// let mut line = Line::from("Hi, what's up?");
|
|
360
|
-
/// assert_eq!(None, line.alignment);
|
|
361
|
-
/// assert_eq!(
|
|
362
|
-
/// Some(Alignment::Right),
|
|
363
|
-
/// line.alignment(Alignment::Right).alignment
|
|
364
|
-
/// )
|
|
365
|
-
/// ```
|
|
366
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
367
|
-
pub fn alignment(self, alignment: Alignment) -> Self {
|
|
368
|
-
Self {
|
|
369
|
-
alignment: Some(alignment),
|
|
370
|
-
..self
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/// Left-aligns this line of text.
|
|
375
|
-
///
|
|
376
|
-
/// Convenience shortcut for `Line::alignment(Alignment::Left)`.
|
|
377
|
-
/// Setting the alignment of a Line generally overrides the alignment of its
|
|
378
|
-
/// parent Text or Widget, with the default alignment being inherited from the parent.
|
|
379
|
-
///
|
|
380
|
-
/// # Examples
|
|
381
|
-
///
|
|
382
|
-
/// ```rust
|
|
383
|
-
/// use ratatui_core::text::Line;
|
|
384
|
-
///
|
|
385
|
-
/// let line = Line::from("Hi, what's up?").left_aligned();
|
|
386
|
-
/// ```
|
|
387
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
388
|
-
pub fn left_aligned(self) -> Self {
|
|
389
|
-
self.alignment(Alignment::Left)
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/// Center-aligns this line of text.
|
|
393
|
-
///
|
|
394
|
-
/// Convenience shortcut for `Line::alignment(Alignment::Center)`.
|
|
395
|
-
/// Setting the alignment of a Line generally overrides the alignment of its
|
|
396
|
-
/// parent Text or Widget, with the default alignment being inherited from the parent.
|
|
397
|
-
///
|
|
398
|
-
/// # Examples
|
|
399
|
-
///
|
|
400
|
-
/// ```rust
|
|
401
|
-
/// use ratatui_core::text::Line;
|
|
402
|
-
///
|
|
403
|
-
/// let line = Line::from("Hi, what's up?").centered();
|
|
404
|
-
/// ```
|
|
405
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
406
|
-
pub fn centered(self) -> Self {
|
|
407
|
-
self.alignment(Alignment::Center)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/// Right-aligns this line of text.
|
|
411
|
-
///
|
|
412
|
-
/// Convenience shortcut for `Line::alignment(Alignment::Right)`.
|
|
413
|
-
/// Setting the alignment of a Line generally overrides the alignment of its
|
|
414
|
-
/// parent Text or Widget, with the default alignment being inherited from the parent.
|
|
415
|
-
///
|
|
416
|
-
/// # Examples
|
|
417
|
-
///
|
|
418
|
-
/// ```rust
|
|
419
|
-
/// use ratatui_core::text::Line;
|
|
420
|
-
///
|
|
421
|
-
/// let line = Line::from("Hi, what's up?").right_aligned();
|
|
422
|
-
/// ```
|
|
423
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
424
|
-
pub fn right_aligned(self) -> Self {
|
|
425
|
-
self.alignment(Alignment::Right)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/// Returns the width of the underlying string.
|
|
429
|
-
///
|
|
430
|
-
/// # Examples
|
|
431
|
-
///
|
|
432
|
-
/// ```rust
|
|
433
|
-
/// use ratatui_core::style::Stylize;
|
|
434
|
-
/// use ratatui_core::text::Line;
|
|
435
|
-
///
|
|
436
|
-
/// let line = Line::from(vec!["Hello".blue(), " world!".green()]);
|
|
437
|
-
/// assert_eq!(12, line.width());
|
|
438
|
-
/// ```
|
|
439
|
-
#[must_use]
|
|
440
|
-
pub fn width(&self) -> usize {
|
|
441
|
-
UnicodeWidthStr::width(self)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/// Returns an iterator over the graphemes held by this line.
|
|
445
|
-
///
|
|
446
|
-
/// `base_style` is the [`Style`] that will be patched with each grapheme [`Style`] to get
|
|
447
|
-
/// the resulting [`Style`].
|
|
448
|
-
///
|
|
449
|
-
/// `base_style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`],
|
|
450
|
-
/// or your own type that implements [`Into<Style>`]).
|
|
451
|
-
///
|
|
452
|
-
/// # Examples
|
|
453
|
-
///
|
|
454
|
-
/// ```rust
|
|
455
|
-
/// use std::iter::Iterator;
|
|
456
|
-
///
|
|
457
|
-
/// use ratatui_core::style::{Color, Style};
|
|
458
|
-
/// use ratatui_core::text::{Line, StyledGrapheme};
|
|
459
|
-
///
|
|
460
|
-
/// let line = Line::styled("Text", Style::default().fg(Color::Yellow));
|
|
461
|
-
/// let style = Style::default().fg(Color::Green).bg(Color::Black);
|
|
462
|
-
/// assert_eq!(
|
|
463
|
-
/// line.styled_graphemes(style)
|
|
464
|
-
/// .collect::<Vec<StyledGrapheme>>(),
|
|
465
|
-
/// vec![
|
|
466
|
-
/// StyledGrapheme::new("T", Style::default().fg(Color::Yellow).bg(Color::Black)),
|
|
467
|
-
/// StyledGrapheme::new("e", Style::default().fg(Color::Yellow).bg(Color::Black)),
|
|
468
|
-
/// StyledGrapheme::new("x", Style::default().fg(Color::Yellow).bg(Color::Black)),
|
|
469
|
-
/// StyledGrapheme::new("t", Style::default().fg(Color::Yellow).bg(Color::Black)),
|
|
470
|
-
/// ]
|
|
471
|
-
/// );
|
|
472
|
-
/// ```
|
|
473
|
-
///
|
|
474
|
-
/// [`Color`]: crate::style::Color
|
|
475
|
-
pub fn styled_graphemes<S: Into<Style>>(
|
|
476
|
-
&'a self,
|
|
477
|
-
base_style: S,
|
|
478
|
-
) -> impl Iterator<Item = StyledGrapheme<'a>> {
|
|
479
|
-
let style = base_style.into().patch(self.style);
|
|
480
|
-
self.spans
|
|
481
|
-
.iter()
|
|
482
|
-
.flat_map(move |span| span.styled_graphemes(style))
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/// Patches the style of this Line, adding modifiers from the given style.
|
|
486
|
-
///
|
|
487
|
-
/// This is useful for when you want to apply a style to a line that already has some styling.
|
|
488
|
-
/// In contrast to [`Line::style`], this method will not overwrite the existing style, but
|
|
489
|
-
/// instead will add the given style's modifiers to this Line's style.
|
|
490
|
-
///
|
|
491
|
-
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
|
|
492
|
-
/// your own type that implements [`Into<Style>`]).
|
|
493
|
-
///
|
|
494
|
-
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
495
|
-
///
|
|
496
|
-
/// # Examples
|
|
497
|
-
///
|
|
498
|
-
/// ```rust
|
|
499
|
-
/// use ratatui_core::style::{Color, Modifier};
|
|
500
|
-
/// use ratatui_core::text::Line;
|
|
501
|
-
///
|
|
502
|
-
/// let line = Line::styled("My text", Modifier::ITALIC);
|
|
503
|
-
///
|
|
504
|
-
/// let styled_line = Line::styled("My text", (Color::Yellow, Modifier::ITALIC));
|
|
505
|
-
///
|
|
506
|
-
/// assert_eq!(styled_line, line.patch_style(Color::Yellow));
|
|
507
|
-
/// ```
|
|
508
|
-
///
|
|
509
|
-
/// [`Color`]: crate::style::Color
|
|
510
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
511
|
-
pub fn patch_style<S: Into<Style>>(mut self, style: S) -> Self {
|
|
512
|
-
self.style = self.style.patch(style);
|
|
513
|
-
self
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/// Resets the style of this Line.
|
|
517
|
-
///
|
|
518
|
-
/// Equivalent to calling `patch_style(Style::reset())`.
|
|
519
|
-
///
|
|
520
|
-
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
521
|
-
///
|
|
522
|
-
/// # Examples
|
|
523
|
-
///
|
|
524
|
-
/// ```rust
|
|
525
|
-
/// # let style = Style::default().yellow();
|
|
526
|
-
/// use ratatui_core::style::{Style, Stylize};
|
|
527
|
-
/// use ratatui_core::text::Line;
|
|
528
|
-
///
|
|
529
|
-
/// let line = Line::styled("My text", style);
|
|
530
|
-
///
|
|
531
|
-
/// assert_eq!(Style::reset(), line.reset_style().style);
|
|
532
|
-
/// ```
|
|
533
|
-
#[must_use = "method moves the value of self and returns the modified value"]
|
|
534
|
-
pub fn reset_style(self) -> Self {
|
|
535
|
-
self.patch_style(Style::reset())
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/// Returns an iterator over the spans of this line.
|
|
539
|
-
pub fn iter(&self) -> core::slice::Iter<'_, Span<'a>> {
|
|
540
|
-
self.spans.iter()
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/// Returns a mutable iterator over the spans of this line.
|
|
544
|
-
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Span<'a>> {
|
|
545
|
-
self.spans.iter_mut()
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/// Adds a span to the line.
|
|
549
|
-
///
|
|
550
|
-
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
|
|
551
|
-
/// `&str`, a `String`, or a `Span`.
|
|
552
|
-
///
|
|
553
|
-
/// # Examples
|
|
554
|
-
///
|
|
555
|
-
/// ```rust
|
|
556
|
-
/// use ratatui_core::text::{Line, Span};
|
|
557
|
-
///
|
|
558
|
-
/// let mut line = Line::from("Hello, ");
|
|
559
|
-
/// line.push_span(Span::raw("world!"));
|
|
560
|
-
/// line.push_span(" How are you?");
|
|
561
|
-
/// ```
|
|
562
|
-
pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
|
|
563
|
-
self.spans.push(span.into());
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
impl UnicodeWidthStr for Line<'_> {
|
|
568
|
-
fn width(&self) -> usize {
|
|
569
|
-
self.spans.iter().map(UnicodeWidthStr::width).sum()
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
fn width_cjk(&self) -> usize {
|
|
573
|
-
self.spans.iter().map(UnicodeWidthStr::width_cjk).sum()
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
impl<'a> IntoIterator for Line<'a> {
|
|
578
|
-
type Item = Span<'a>;
|
|
579
|
-
type IntoIter = alloc::vec::IntoIter<Span<'a>>;
|
|
580
|
-
|
|
581
|
-
fn into_iter(self) -> Self::IntoIter {
|
|
582
|
-
self.spans.into_iter()
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
impl<'a> IntoIterator for &'a Line<'a> {
|
|
587
|
-
type Item = &'a Span<'a>;
|
|
588
|
-
type IntoIter = core::slice::Iter<'a, Span<'a>>;
|
|
589
|
-
|
|
590
|
-
fn into_iter(self) -> Self::IntoIter {
|
|
591
|
-
self.iter()
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
impl<'a> IntoIterator for &'a mut Line<'a> {
|
|
596
|
-
type Item = &'a mut Span<'a>;
|
|
597
|
-
type IntoIter = core::slice::IterMut<'a, Span<'a>>;
|
|
598
|
-
|
|
599
|
-
fn into_iter(self) -> Self::IntoIter {
|
|
600
|
-
self.iter_mut()
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
impl From<String> for Line<'_> {
|
|
605
|
-
fn from(s: String) -> Self {
|
|
606
|
-
Self::raw(s)
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
impl<'a> From<&'a str> for Line<'a> {
|
|
611
|
-
fn from(s: &'a str) -> Self {
|
|
612
|
-
Self::raw(s)
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
impl<'a> From<Cow<'a, str>> for Line<'a> {
|
|
617
|
-
fn from(s: Cow<'a, str>) -> Self {
|
|
618
|
-
Self::raw(s)
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
impl<'a> From<Vec<Span<'a>>> for Line<'a> {
|
|
623
|
-
fn from(spans: Vec<Span<'a>>) -> Self {
|
|
624
|
-
Self {
|
|
625
|
-
spans,
|
|
626
|
-
..Default::default()
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
impl<'a> From<Span<'a>> for Line<'a> {
|
|
632
|
-
fn from(span: Span<'a>) -> Self {
|
|
633
|
-
Self::from(vec![span])
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
impl<'a> From<Line<'a>> for String {
|
|
638
|
-
fn from(line: Line<'a>) -> Self {
|
|
639
|
-
line.iter().fold(Self::new(), |mut acc, s| {
|
|
640
|
-
acc.push_str(s.content.as_ref());
|
|
641
|
-
acc
|
|
642
|
-
})
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
impl<'a, T> FromIterator<T> for Line<'a>
|
|
647
|
-
where
|
|
648
|
-
T: Into<Span<'a>>,
|
|
649
|
-
{
|
|
650
|
-
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
|
651
|
-
Self::from(iter.into_iter().map(Into::into).collect::<Vec<_>>())
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/// Adds a `Span` to a `Line`, returning a new `Line` with the `Span` added.
|
|
656
|
-
impl<'a> core::ops::Add<Span<'a>> for Line<'a> {
|
|
657
|
-
type Output = Self;
|
|
658
|
-
|
|
659
|
-
fn add(mut self, rhs: Span<'a>) -> Self::Output {
|
|
660
|
-
self.spans.push(rhs);
|
|
661
|
-
self
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/// Adds two `Line`s together, returning a new `Text` with the contents of the two `Line`s.
|
|
666
|
-
impl<'a> core::ops::Add<Self> for Line<'a> {
|
|
667
|
-
type Output = Text<'a>;
|
|
668
|
-
|
|
669
|
-
fn add(self, rhs: Self) -> Self::Output {
|
|
670
|
-
Text::from(vec![self, rhs])
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
impl<'a> core::ops::AddAssign<Span<'a>> for Line<'a> {
|
|
675
|
-
fn add_assign(&mut self, rhs: Span<'a>) {
|
|
676
|
-
self.spans.push(rhs);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
impl<'a> Extend<Span<'a>> for Line<'a> {
|
|
681
|
-
fn extend<T: IntoIterator<Item = Span<'a>>>(&mut self, iter: T) {
|
|
682
|
-
self.spans.extend(iter);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
impl Widget for Line<'_> {
|
|
687
|
-
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
688
|
-
Widget::render(&self, area, buf);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
impl Widget for &Line<'_> {
|
|
693
|
-
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
694
|
-
self.render_with_alignment(area, buf, None);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
impl Line<'_> {
|
|
699
|
-
/// An internal implementation method for `Widget::render` that allows the parent widget to
|
|
700
|
-
/// define a default alignment, to be used if `Line::alignment` is `None`.
|
|
701
|
-
pub(crate) fn render_with_alignment(
|
|
702
|
-
&self,
|
|
703
|
-
area: Rect,
|
|
704
|
-
buf: &mut Buffer,
|
|
705
|
-
parent_alignment: Option<Alignment>,
|
|
706
|
-
) {
|
|
707
|
-
let area = area.intersection(buf.area);
|
|
708
|
-
if area.is_empty() {
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
let area = Rect { height: 1, ..area };
|
|
712
|
-
let line_width = self.width();
|
|
713
|
-
if line_width == 0 {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
buf.set_style(area, self.style);
|
|
718
|
-
|
|
719
|
-
let alignment = self.alignment.or(parent_alignment);
|
|
720
|
-
|
|
721
|
-
let area_width = usize::from(area.width);
|
|
722
|
-
let can_render_complete_line = line_width <= area_width;
|
|
723
|
-
if can_render_complete_line {
|
|
724
|
-
let indent_width = match alignment {
|
|
725
|
-
Some(Alignment::Center) => (area_width.saturating_sub(line_width)) / 2,
|
|
726
|
-
Some(Alignment::Right) => area_width.saturating_sub(line_width),
|
|
727
|
-
Some(Alignment::Left) | None => 0,
|
|
728
|
-
};
|
|
729
|
-
let indent_width = u16::try_from(indent_width).unwrap_or(u16::MAX);
|
|
730
|
-
let area = area.indent_x(indent_width);
|
|
731
|
-
render_spans(&self.spans, area, buf, 0);
|
|
732
|
-
} else {
|
|
733
|
-
// There is not enough space to render the whole line. As the right side is truncated by
|
|
734
|
-
// the area width, only truncate the left.
|
|
735
|
-
let skip_width = match alignment {
|
|
736
|
-
Some(Alignment::Center) => (line_width.saturating_sub(area_width)) / 2,
|
|
737
|
-
Some(Alignment::Right) => line_width.saturating_sub(area_width),
|
|
738
|
-
Some(Alignment::Left) | None => 0,
|
|
739
|
-
};
|
|
740
|
-
render_spans(&self.spans, area, buf, skip_width);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/// Renders all the spans of the line that should be visible.
|
|
746
|
-
fn render_spans(spans: &[Span], mut area: Rect, buf: &mut Buffer, span_skip_width: usize) {
|
|
747
|
-
for (span, span_width, offset) in spans_after_width(spans, span_skip_width) {
|
|
748
|
-
area = area.indent_x(offset);
|
|
749
|
-
if area.is_empty() {
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
span.render(area, buf);
|
|
753
|
-
let span_width = u16::try_from(span_width).unwrap_or(u16::MAX);
|
|
754
|
-
area = area.indent_x(span_width);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
/// Returns an iterator over the spans that lie after a given skip width from the start of the
|
|
759
|
-
/// `Line` (including a partially visible span if the `skip_width` lands within a span).
|
|
760
|
-
fn spans_after_width<'a>(
|
|
761
|
-
spans: &'a [Span],
|
|
762
|
-
mut skip_width: usize,
|
|
763
|
-
) -> impl Iterator<Item = (Span<'a>, usize, u16)> {
|
|
764
|
-
spans
|
|
765
|
-
.iter()
|
|
766
|
-
.map(|span| (span, span.width()))
|
|
767
|
-
// Filter non visible spans out.
|
|
768
|
-
.filter_map(move |(span, span_width)| {
|
|
769
|
-
// Ignore spans that are completely before the offset. Decrement `span_skip_width` by
|
|
770
|
-
// the span width until we find a span that is partially or completely visible.
|
|
771
|
-
if skip_width >= span_width {
|
|
772
|
-
skip_width = skip_width.saturating_sub(span_width);
|
|
773
|
-
return None;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// Apply the skip from the start of the span, not the end as the end will be trimmed
|
|
777
|
-
// when rendering the span to the buffer.
|
|
778
|
-
let available_width = span_width.saturating_sub(skip_width);
|
|
779
|
-
skip_width = 0; // ensure the next span is rendered in full
|
|
780
|
-
Some((span, span_width, available_width))
|
|
781
|
-
})
|
|
782
|
-
.map(|(span, span_width, available_width)| {
|
|
783
|
-
if span_width <= available_width {
|
|
784
|
-
// Span is fully visible. Clone here is fast as the underlying content is `Cow`.
|
|
785
|
-
return (span.clone(), span_width, 0u16);
|
|
786
|
-
}
|
|
787
|
-
// Span is only partially visible. As the end is truncated by the area width, only
|
|
788
|
-
// truncate the start of the span.
|
|
789
|
-
let (content, actual_width) = span.content.unicode_truncate_start(available_width);
|
|
790
|
-
|
|
791
|
-
// When the first grapheme of the span was truncated, start rendering from a position
|
|
792
|
-
// that takes that into account by indenting the start of the area
|
|
793
|
-
let first_grapheme_offset = available_width.saturating_sub(actual_width);
|
|
794
|
-
let first_grapheme_offset = u16::try_from(first_grapheme_offset).unwrap_or(u16::MAX);
|
|
795
|
-
(
|
|
796
|
-
Span::styled(content, span.style),
|
|
797
|
-
actual_width,
|
|
798
|
-
first_grapheme_offset,
|
|
799
|
-
)
|
|
800
|
-
})
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
/// A trait for converting a value to a [`Line`].
|
|
804
|
-
///
|
|
805
|
-
/// This trait is automatically implemented for any type that implements the [`Display`] trait. As
|
|
806
|
-
/// such, `ToLine` shouln't be implemented directly: [`Display`] should be implemented instead, and
|
|
807
|
-
/// you get the `ToLine` implementation for free.
|
|
808
|
-
///
|
|
809
|
-
/// [`Display`]: std::fmt::Display
|
|
810
|
-
pub trait ToLine {
|
|
811
|
-
/// Converts the value to a [`Line`].
|
|
812
|
-
fn to_line(&self) -> Line<'_>;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/// # Panics
|
|
816
|
-
///
|
|
817
|
-
/// In this implementation, the `to_line` method panics if the `Display` implementation returns an
|
|
818
|
-
/// error. This indicates an incorrect `Display` implementation since `fmt::Write for String` never
|
|
819
|
-
/// returns an error itself.
|
|
820
|
-
impl<T: fmt::Display> ToLine for T {
|
|
821
|
-
fn to_line(&self) -> Line<'_> {
|
|
822
|
-
Line::from(self.to_string())
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
impl fmt::Display for Line<'_> {
|
|
827
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
828
|
-
for span in &self.spans {
|
|
829
|
-
write!(f, "{span}")?;
|
|
830
|
-
}
|
|
831
|
-
Ok(())
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
impl Styled for Line<'_> {
|
|
836
|
-
type Item = Self;
|
|
837
|
-
|
|
838
|
-
fn style(&self) -> Style {
|
|
839
|
-
self.style
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
|
|
843
|
-
self.style(style)
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
#[cfg(test)]
|
|
848
|
-
mod tests {
|
|
849
|
-
use alloc::format;
|
|
850
|
-
use core::iter;
|
|
851
|
-
use std::dbg;
|
|
852
|
-
|
|
853
|
-
use rstest::{fixture, rstest};
|
|
854
|
-
|
|
855
|
-
use super::*;
|
|
856
|
-
use crate::style::{Color, Modifier, Stylize};
|
|
857
|
-
|
|
858
|
-
#[fixture]
|
|
859
|
-
fn small_buf() -> Buffer {
|
|
860
|
-
Buffer::empty(Rect::new(0, 0, 10, 1))
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
#[test]
|
|
864
|
-
fn raw_str() {
|
|
865
|
-
let line = Line::raw("test content");
|
|
866
|
-
assert_eq!(line.spans, [Span::raw("test content")]);
|
|
867
|
-
assert_eq!(line.alignment, None);
|
|
868
|
-
|
|
869
|
-
let line = Line::raw("a\nb");
|
|
870
|
-
assert_eq!(line.spans, [Span::raw("a"), Span::raw("b")]);
|
|
871
|
-
assert_eq!(line.alignment, None);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
#[test]
|
|
875
|
-
fn styled_str() {
|
|
876
|
-
let style = Style::new().yellow();
|
|
877
|
-
let content = "Hello, world!";
|
|
878
|
-
let line = Line::styled(content, style);
|
|
879
|
-
assert_eq!(line.spans, [Span::raw(content)]);
|
|
880
|
-
assert_eq!(line.style, style);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
#[test]
|
|
884
|
-
fn styled_string() {
|
|
885
|
-
let style = Style::new().yellow();
|
|
886
|
-
let content = String::from("Hello, world!");
|
|
887
|
-
let line = Line::styled(content.clone(), style);
|
|
888
|
-
assert_eq!(line.spans, [Span::raw(content)]);
|
|
889
|
-
assert_eq!(line.style, style);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
#[test]
|
|
893
|
-
fn styled_cow() {
|
|
894
|
-
let style = Style::new().yellow();
|
|
895
|
-
let content = Cow::from("Hello, world!");
|
|
896
|
-
let line = Line::styled(content.clone(), style);
|
|
897
|
-
assert_eq!(line.spans, [Span::raw(content)]);
|
|
898
|
-
assert_eq!(line.style, style);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
#[test]
|
|
902
|
-
fn spans_vec() {
|
|
903
|
-
let line = Line::default().spans(vec!["Hello".blue(), " world!".green()]);
|
|
904
|
-
assert_eq!(
|
|
905
|
-
line.spans,
|
|
906
|
-
vec![
|
|
907
|
-
Span::styled("Hello", Style::new().blue()),
|
|
908
|
-
Span::styled(" world!", Style::new().green()),
|
|
909
|
-
]
|
|
910
|
-
);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
#[test]
|
|
914
|
-
fn spans_iter() {
|
|
915
|
-
let line = Line::default().spans([1, 2, 3].iter().map(|i| format!("Item {i}")));
|
|
916
|
-
assert_eq!(
|
|
917
|
-
line.spans,
|
|
918
|
-
vec![
|
|
919
|
-
Span::raw("Item 1"),
|
|
920
|
-
Span::raw("Item 2"),
|
|
921
|
-
Span::raw("Item 3"),
|
|
922
|
-
]
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
#[test]
|
|
927
|
-
fn style() {
|
|
928
|
-
let line = Line::default().style(Style::new().red());
|
|
929
|
-
assert_eq!(line.style, Style::new().red());
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
#[test]
|
|
933
|
-
fn alignment() {
|
|
934
|
-
let line = Line::from("This is left").alignment(Alignment::Left);
|
|
935
|
-
assert_eq!(Some(Alignment::Left), line.alignment);
|
|
936
|
-
|
|
937
|
-
let line = Line::from("This is default");
|
|
938
|
-
assert_eq!(None, line.alignment);
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
#[test]
|
|
942
|
-
fn width() {
|
|
943
|
-
let line = Line::from(vec![
|
|
944
|
-
Span::styled("My", Style::default().fg(Color::Yellow)),
|
|
945
|
-
Span::raw(" text"),
|
|
946
|
-
]);
|
|
947
|
-
assert_eq!(7, line.width());
|
|
948
|
-
|
|
949
|
-
let empty_line = Line::default();
|
|
950
|
-
assert_eq!(0, empty_line.width());
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
#[test]
|
|
954
|
-
fn patch_style() {
|
|
955
|
-
let raw_line = Line::styled("foobar", Color::Yellow);
|
|
956
|
-
let styled_line = Line::styled("foobar", (Color::Yellow, Modifier::ITALIC));
|
|
957
|
-
|
|
958
|
-
assert_ne!(raw_line, styled_line);
|
|
959
|
-
|
|
960
|
-
let raw_line = raw_line.patch_style(Modifier::ITALIC);
|
|
961
|
-
assert_eq!(raw_line, styled_line);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
#[test]
|
|
965
|
-
fn reset_style() {
|
|
966
|
-
let line =
|
|
967
|
-
Line::styled("foobar", Style::default().yellow().on_red().italic()).reset_style();
|
|
968
|
-
|
|
969
|
-
assert_eq!(Style::reset(), line.style);
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
#[test]
|
|
973
|
-
fn stylize() {
|
|
974
|
-
assert_eq!(Line::default().green().style, Color::Green.into());
|
|
975
|
-
assert_eq!(
|
|
976
|
-
Line::default().on_green().style,
|
|
977
|
-
Style::new().bg(Color::Green)
|
|
978
|
-
);
|
|
979
|
-
assert_eq!(Line::default().italic().style, Modifier::ITALIC.into());
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
#[test]
|
|
983
|
-
fn from_string() {
|
|
984
|
-
let s = String::from("Hello, world!");
|
|
985
|
-
let line = Line::from(s);
|
|
986
|
-
assert_eq!(line.spans, [Span::from("Hello, world!")]);
|
|
987
|
-
|
|
988
|
-
let s = String::from("Hello\nworld!");
|
|
989
|
-
let line = Line::from(s);
|
|
990
|
-
assert_eq!(line.spans, [Span::from("Hello"), Span::from("world!")]);
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
#[test]
|
|
994
|
-
fn from_str() {
|
|
995
|
-
let s = "Hello, world!";
|
|
996
|
-
let line = Line::from(s);
|
|
997
|
-
assert_eq!(line.spans, [Span::from("Hello, world!")]);
|
|
998
|
-
|
|
999
|
-
let s = "Hello\nworld!";
|
|
1000
|
-
let line = Line::from(s);
|
|
1001
|
-
assert_eq!(line.spans, [Span::from("Hello"), Span::from("world!")]);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
#[test]
|
|
1005
|
-
fn to_line() {
|
|
1006
|
-
let line = 42.to_line();
|
|
1007
|
-
assert_eq!(line.spans, [Span::from("42")]);
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
#[test]
|
|
1011
|
-
fn from_vec() {
|
|
1012
|
-
let spans = vec![
|
|
1013
|
-
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
|
1014
|
-
Span::styled(" world!", Style::default().fg(Color::Green)),
|
|
1015
|
-
];
|
|
1016
|
-
let line = Line::from(spans.clone());
|
|
1017
|
-
assert_eq!(line.spans, spans);
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
#[test]
|
|
1021
|
-
fn from_iter() {
|
|
1022
|
-
let line = Line::from_iter(vec!["Hello".blue(), " world!".green()]);
|
|
1023
|
-
assert_eq!(
|
|
1024
|
-
line.spans,
|
|
1025
|
-
vec![
|
|
1026
|
-
Span::styled("Hello", Style::new().blue()),
|
|
1027
|
-
Span::styled(" world!", Style::new().green()),
|
|
1028
|
-
]
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
#[test]
|
|
1033
|
-
fn collect() {
|
|
1034
|
-
let line: Line = iter::once("Hello".blue())
|
|
1035
|
-
.chain(iter::once(" world!".green()))
|
|
1036
|
-
.collect();
|
|
1037
|
-
assert_eq!(
|
|
1038
|
-
line.spans,
|
|
1039
|
-
vec![
|
|
1040
|
-
Span::styled("Hello", Style::new().blue()),
|
|
1041
|
-
Span::styled(" world!", Style::new().green()),
|
|
1042
|
-
]
|
|
1043
|
-
);
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
#[test]
|
|
1047
|
-
fn from_span() {
|
|
1048
|
-
let span = Span::styled("Hello, world!", Style::default().fg(Color::Yellow));
|
|
1049
|
-
let line = Line::from(span.clone());
|
|
1050
|
-
assert_eq!(line.spans, [span]);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
#[test]
|
|
1054
|
-
fn add_span() {
|
|
1055
|
-
assert_eq!(
|
|
1056
|
-
Line::raw("Red").red() + Span::raw("blue").blue(),
|
|
1057
|
-
Line {
|
|
1058
|
-
spans: vec![Span::raw("Red"), Span::raw("blue").blue()],
|
|
1059
|
-
style: Style::new().red(),
|
|
1060
|
-
alignment: None,
|
|
1061
|
-
},
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
#[test]
|
|
1066
|
-
fn add_line() {
|
|
1067
|
-
assert_eq!(
|
|
1068
|
-
Line::raw("Red").red() + Line::raw("Blue").blue(),
|
|
1069
|
-
Text {
|
|
1070
|
-
lines: vec![Line::raw("Red").red(), Line::raw("Blue").blue()],
|
|
1071
|
-
style: Style::default(),
|
|
1072
|
-
alignment: None,
|
|
1073
|
-
}
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
#[test]
|
|
1078
|
-
fn add_assign_span() {
|
|
1079
|
-
let mut line = Line::raw("Red").red();
|
|
1080
|
-
line += Span::raw("Blue").blue();
|
|
1081
|
-
assert_eq!(
|
|
1082
|
-
line,
|
|
1083
|
-
Line {
|
|
1084
|
-
spans: vec![Span::raw("Red"), Span::raw("Blue").blue()],
|
|
1085
|
-
style: Style::new().red(),
|
|
1086
|
-
alignment: None,
|
|
1087
|
-
},
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
#[test]
|
|
1092
|
-
fn extend() {
|
|
1093
|
-
let mut line = Line::from("Hello, ");
|
|
1094
|
-
line.extend([Span::raw("world!")]);
|
|
1095
|
-
assert_eq!(line.spans, [Span::raw("Hello, "), Span::raw("world!")]);
|
|
1096
|
-
|
|
1097
|
-
let mut line = Line::from("Hello, ");
|
|
1098
|
-
line.extend([Span::raw("world! "), Span::raw("How are you?")]);
|
|
1099
|
-
assert_eq!(
|
|
1100
|
-
line.spans,
|
|
1101
|
-
[
|
|
1102
|
-
Span::raw("Hello, "),
|
|
1103
|
-
Span::raw("world! "),
|
|
1104
|
-
Span::raw("How are you?")
|
|
1105
|
-
]
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
#[test]
|
|
1110
|
-
fn into_string() {
|
|
1111
|
-
let line = Line::from(vec![
|
|
1112
|
-
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
|
1113
|
-
Span::styled(" world!", Style::default().fg(Color::Green)),
|
|
1114
|
-
]);
|
|
1115
|
-
let s: String = line.into();
|
|
1116
|
-
assert_eq!(s, "Hello, world!");
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
#[test]
|
|
1120
|
-
fn styled_graphemes() {
|
|
1121
|
-
const RED: Style = Style::new().red();
|
|
1122
|
-
const GREEN: Style = Style::new().green();
|
|
1123
|
-
const BLUE: Style = Style::new().blue();
|
|
1124
|
-
const RED_ON_WHITE: Style = Style::new().red().on_white();
|
|
1125
|
-
const GREEN_ON_WHITE: Style = Style::new().green().on_white();
|
|
1126
|
-
const BLUE_ON_WHITE: Style = Style::new().blue().on_white();
|
|
1127
|
-
|
|
1128
|
-
let line = Line::from(vec![
|
|
1129
|
-
Span::styled("He", RED),
|
|
1130
|
-
Span::styled("ll", GREEN),
|
|
1131
|
-
Span::styled("o!", BLUE),
|
|
1132
|
-
]);
|
|
1133
|
-
let styled_graphemes = line
|
|
1134
|
-
.styled_graphemes(Style::new().bg(Color::White))
|
|
1135
|
-
.collect::<Vec<StyledGrapheme>>();
|
|
1136
|
-
assert_eq!(
|
|
1137
|
-
styled_graphemes,
|
|
1138
|
-
vec![
|
|
1139
|
-
StyledGrapheme::new("H", RED_ON_WHITE),
|
|
1140
|
-
StyledGrapheme::new("e", RED_ON_WHITE),
|
|
1141
|
-
StyledGrapheme::new("l", GREEN_ON_WHITE),
|
|
1142
|
-
StyledGrapheme::new("l", GREEN_ON_WHITE),
|
|
1143
|
-
StyledGrapheme::new("o", BLUE_ON_WHITE),
|
|
1144
|
-
StyledGrapheme::new("!", BLUE_ON_WHITE),
|
|
1145
|
-
],
|
|
1146
|
-
);
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
#[test]
|
|
1150
|
-
fn display_line_from_vec() {
|
|
1151
|
-
let line_from_vec = Line::from(vec![Span::raw("Hello,"), Span::raw(" world!")]);
|
|
1152
|
-
|
|
1153
|
-
assert_eq!(format!("{line_from_vec}"), "Hello, world!");
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
#[test]
|
|
1157
|
-
fn display_styled_line() {
|
|
1158
|
-
let styled_line = Line::styled("Hello, world!", Style::new().green().italic());
|
|
1159
|
-
|
|
1160
|
-
assert_eq!(format!("{styled_line}"), "Hello, world!");
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
#[test]
|
|
1164
|
-
fn display_line_from_styled_span() {
|
|
1165
|
-
let styled_span = Span::styled("Hello, world!", Style::new().green().italic());
|
|
1166
|
-
let line_from_styled_span = Line::from(styled_span);
|
|
1167
|
-
|
|
1168
|
-
assert_eq!(format!("{line_from_styled_span}"), "Hello, world!");
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
#[test]
|
|
1172
|
-
fn left_aligned() {
|
|
1173
|
-
let line = Line::from("Hello, world!").left_aligned();
|
|
1174
|
-
assert_eq!(line.alignment, Some(Alignment::Left));
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
#[test]
|
|
1178
|
-
fn centered() {
|
|
1179
|
-
let line = Line::from("Hello, world!").centered();
|
|
1180
|
-
assert_eq!(line.alignment, Some(Alignment::Center));
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
#[test]
|
|
1184
|
-
fn right_aligned() {
|
|
1185
|
-
let line = Line::from("Hello, world!").right_aligned();
|
|
1186
|
-
assert_eq!(line.alignment, Some(Alignment::Right));
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
#[test]
|
|
1190
|
-
pub fn push_span() {
|
|
1191
|
-
let mut line = Line::from("A");
|
|
1192
|
-
line.push_span(Span::raw("B"));
|
|
1193
|
-
line.push_span("C");
|
|
1194
|
-
assert_eq!(
|
|
1195
|
-
line.spans,
|
|
1196
|
-
vec![Span::raw("A"), Span::raw("B"), Span::raw("C")]
|
|
1197
|
-
);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
mod widget {
|
|
1201
|
-
use unicode_segmentation::UnicodeSegmentation;
|
|
1202
|
-
use unicode_width::UnicodeWidthStr;
|
|
1203
|
-
|
|
1204
|
-
use super::*;
|
|
1205
|
-
use crate::buffer::Cell;
|
|
1206
|
-
|
|
1207
|
-
const BLUE: Style = Style::new().blue();
|
|
1208
|
-
const GREEN: Style = Style::new().green();
|
|
1209
|
-
const ITALIC: Style = Style::new().italic();
|
|
1210
|
-
|
|
1211
|
-
#[fixture]
|
|
1212
|
-
fn hello_world() -> Line<'static> {
|
|
1213
|
-
Line::from(vec![
|
|
1214
|
-
Span::styled("Hello ", BLUE),
|
|
1215
|
-
Span::styled("world!", GREEN),
|
|
1216
|
-
])
|
|
1217
|
-
.style(ITALIC)
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
#[test]
|
|
1221
|
-
fn render() {
|
|
1222
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 1));
|
|
1223
|
-
hello_world().render(Rect::new(0, 0, 15, 1), &mut buf);
|
|
1224
|
-
let mut expected = Buffer::with_lines(["Hello world! "]);
|
|
1225
|
-
expected.set_style(Rect::new(0, 0, 15, 1), ITALIC);
|
|
1226
|
-
expected.set_style(Rect::new(0, 0, 6, 1), BLUE);
|
|
1227
|
-
expected.set_style(Rect::new(6, 0, 6, 1), GREEN);
|
|
1228
|
-
assert_eq!(buf, expected);
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
#[rstest]
|
|
1232
|
-
fn render_out_of_bounds(hello_world: Line<'static>, mut small_buf: Buffer) {
|
|
1233
|
-
let out_of_bounds = Rect::new(20, 20, 10, 1);
|
|
1234
|
-
hello_world.render(out_of_bounds, &mut small_buf);
|
|
1235
|
-
assert_eq!(small_buf, Buffer::empty(small_buf.area));
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
#[test]
|
|
1239
|
-
fn render_only_styles_line_area() {
|
|
1240
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 1));
|
|
1241
|
-
hello_world().render(Rect::new(0, 0, 15, 1), &mut buf);
|
|
1242
|
-
let mut expected = Buffer::with_lines(["Hello world! "]);
|
|
1243
|
-
expected.set_style(Rect::new(0, 0, 15, 1), ITALIC);
|
|
1244
|
-
expected.set_style(Rect::new(0, 0, 6, 1), BLUE);
|
|
1245
|
-
expected.set_style(Rect::new(6, 0, 6, 1), GREEN);
|
|
1246
|
-
assert_eq!(buf, expected);
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
#[test]
|
|
1250
|
-
fn render_only_styles_first_line() {
|
|
1251
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 2));
|
|
1252
|
-
hello_world().render(buf.area, &mut buf);
|
|
1253
|
-
let mut expected = Buffer::with_lines(["Hello world! ", " "]);
|
|
1254
|
-
expected.set_style(Rect::new(0, 0, 20, 1), ITALIC);
|
|
1255
|
-
expected.set_style(Rect::new(0, 0, 6, 1), BLUE);
|
|
1256
|
-
expected.set_style(Rect::new(6, 0, 6, 1), GREEN);
|
|
1257
|
-
assert_eq!(buf, expected);
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
#[test]
|
|
1261
|
-
fn render_truncates() {
|
|
1262
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 1));
|
|
1263
|
-
Line::from("Hello world!").render(Rect::new(0, 0, 5, 1), &mut buf);
|
|
1264
|
-
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
#[test]
|
|
1268
|
-
fn render_centered() {
|
|
1269
|
-
let line = hello_world().alignment(Alignment::Center);
|
|
1270
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 1));
|
|
1271
|
-
line.render(Rect::new(0, 0, 15, 1), &mut buf);
|
|
1272
|
-
let mut expected = Buffer::with_lines([" Hello world! "]);
|
|
1273
|
-
expected.set_style(Rect::new(0, 0, 15, 1), ITALIC);
|
|
1274
|
-
expected.set_style(Rect::new(1, 0, 6, 1), BLUE);
|
|
1275
|
-
expected.set_style(Rect::new(7, 0, 6, 1), GREEN);
|
|
1276
|
-
assert_eq!(buf, expected);
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
#[test]
|
|
1280
|
-
fn render_right_aligned() {
|
|
1281
|
-
let line = hello_world().alignment(Alignment::Right);
|
|
1282
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 1));
|
|
1283
|
-
line.render(Rect::new(0, 0, 15, 1), &mut buf);
|
|
1284
|
-
let mut expected = Buffer::with_lines([" Hello world!"]);
|
|
1285
|
-
expected.set_style(Rect::new(0, 0, 15, 1), ITALIC);
|
|
1286
|
-
expected.set_style(Rect::new(3, 0, 6, 1), BLUE);
|
|
1287
|
-
expected.set_style(Rect::new(9, 0, 6, 1), GREEN);
|
|
1288
|
-
assert_eq!(buf, expected);
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
#[test]
|
|
1292
|
-
fn render_truncates_left() {
|
|
1293
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
1294
|
-
Line::from("Hello world")
|
|
1295
|
-
.left_aligned()
|
|
1296
|
-
.render(buf.area, &mut buf);
|
|
1297
|
-
assert_eq!(buf, Buffer::with_lines(["Hello"]));
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
#[test]
|
|
1301
|
-
fn render_truncates_right() {
|
|
1302
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
1303
|
-
Line::from("Hello world")
|
|
1304
|
-
.right_aligned()
|
|
1305
|
-
.render(buf.area, &mut buf);
|
|
1306
|
-
assert_eq!(buf, Buffer::with_lines(["world"]));
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
#[test]
|
|
1310
|
-
fn render_truncates_center() {
|
|
1311
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
1312
|
-
Line::from("Hello world")
|
|
1313
|
-
.centered()
|
|
1314
|
-
.render(buf.area, &mut buf);
|
|
1315
|
-
assert_eq!(buf, Buffer::with_lines(["lo wo"]));
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1319
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1320
|
-
#[test]
|
|
1321
|
-
fn regression_1032() {
|
|
1322
|
-
let line = Line::from(
|
|
1323
|
-
"🦀 RFC8628 OAuth 2.0 Device Authorization GrantでCLIからGithubのaccess tokenを取得する",
|
|
1324
|
-
);
|
|
1325
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 83, 1));
|
|
1326
|
-
line.render(buf.area, &mut buf);
|
|
1327
|
-
assert_eq!(
|
|
1328
|
-
buf,
|
|
1329
|
-
Buffer::with_lines([
|
|
1330
|
-
"🦀 RFC8628 OAuth 2.0 Device Authorization GrantでCLIからGithubのaccess tokenを取得 "
|
|
1331
|
-
])
|
|
1332
|
-
);
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
/// Documentary test to highlight the crab emoji width / length discrepancy
|
|
1336
|
-
///
|
|
1337
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1338
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1339
|
-
#[test]
|
|
1340
|
-
fn crab_emoji_width() {
|
|
1341
|
-
let crab = "🦀";
|
|
1342
|
-
assert_eq!(crab.len(), 4); // bytes
|
|
1343
|
-
assert_eq!(crab.chars().count(), 1);
|
|
1344
|
-
assert_eq!(crab.graphemes(true).count(), 1);
|
|
1345
|
-
assert_eq!(crab.width(), 2); // display width
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1349
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1350
|
-
#[rstest]
|
|
1351
|
-
#[case::left_4(Alignment::Left, 4, "1234")]
|
|
1352
|
-
#[case::left_5(Alignment::Left, 5, "1234 ")]
|
|
1353
|
-
#[case::left_6(Alignment::Left, 6, "1234🦀")]
|
|
1354
|
-
#[case::left_7(Alignment::Left, 7, "1234🦀7")]
|
|
1355
|
-
#[case::right_4(Alignment::Right, 4, "7890")]
|
|
1356
|
-
#[case::right_5(Alignment::Right, 5, " 7890")]
|
|
1357
|
-
#[case::right_6(Alignment::Right, 6, "🦀7890")]
|
|
1358
|
-
#[case::right_7(Alignment::Right, 7, "4🦀7890")]
|
|
1359
|
-
fn render_truncates_emoji(
|
|
1360
|
-
#[case] alignment: Alignment,
|
|
1361
|
-
#[case] buf_width: u16,
|
|
1362
|
-
#[case] expected: &str,
|
|
1363
|
-
) {
|
|
1364
|
-
let line = Line::from("1234🦀7890").alignment(alignment);
|
|
1365
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
|
1366
|
-
line.render(buf.area, &mut buf);
|
|
1367
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1371
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1372
|
-
///
|
|
1373
|
-
/// centering is tricky because there's an ambiguity about whether to take one more char
|
|
1374
|
-
/// from the left or the right when the line width is odd. This interacts with the width of
|
|
1375
|
-
/// the crab emoji, which is 2 characters wide by hitting the left or right side of the
|
|
1376
|
-
/// emoji.
|
|
1377
|
-
#[rstest]
|
|
1378
|
-
#[case::center_6_0(6, 0, "")]
|
|
1379
|
-
#[case::center_6_1(6, 1, " ")] // lef side of "🦀"
|
|
1380
|
-
#[case::center_6_2(6, 2, "🦀")]
|
|
1381
|
-
#[case::center_6_3(6, 3, "b🦀")]
|
|
1382
|
-
#[case::center_6_4(6, 4, "b🦀c")]
|
|
1383
|
-
#[case::center_7_0(7, 0, "")]
|
|
1384
|
-
#[case::center_7_1(7, 1, " ")] // right side of "🦀"
|
|
1385
|
-
#[case::center_7_2(7, 2, "🦀")]
|
|
1386
|
-
#[case::center_7_3(7, 3, "🦀c")]
|
|
1387
|
-
#[case::center_7_4(7, 4, "b🦀c")]
|
|
1388
|
-
#[case::center_8_0(8, 0, "")]
|
|
1389
|
-
#[case::center_8_1(8, 1, " ")] // right side of "🦀"
|
|
1390
|
-
#[case::center_8_2(8, 2, " c")] // right side of "🦀c"
|
|
1391
|
-
#[case::center_8_3(8, 3, "🦀c")]
|
|
1392
|
-
#[case::center_8_4(8, 4, "🦀cd")]
|
|
1393
|
-
#[case::center_8_5(8, 5, "b🦀cd")]
|
|
1394
|
-
#[case::center_9_0(9, 0, "")]
|
|
1395
|
-
#[case::center_9_1(9, 1, "c")]
|
|
1396
|
-
#[case::center_9_2(9, 2, " c")] // right side of "🦀c"
|
|
1397
|
-
#[case::center_9_3(9, 3, " cd")]
|
|
1398
|
-
#[case::center_9_4(9, 4, "🦀cd")]
|
|
1399
|
-
#[case::center_9_5(9, 5, "🦀cde")]
|
|
1400
|
-
#[case::center_9_6(9, 6, "b🦀cde")]
|
|
1401
|
-
fn render_truncates_emoji_center(
|
|
1402
|
-
#[case] line_width: u16,
|
|
1403
|
-
#[case] buf_width: u16,
|
|
1404
|
-
#[case] expected: &str,
|
|
1405
|
-
) {
|
|
1406
|
-
// because the crab emoji is 2 characters wide, it will can cause the centering tests
|
|
1407
|
-
// intersect with either the left or right part of the emoji, which causes the emoji to
|
|
1408
|
-
// be not rendered. Checking for four different widths of the line is enough to cover
|
|
1409
|
-
// all the possible cases.
|
|
1410
|
-
let value = match line_width {
|
|
1411
|
-
6 => "ab🦀cd",
|
|
1412
|
-
7 => "ab🦀cde",
|
|
1413
|
-
8 => "ab🦀cdef",
|
|
1414
|
-
9 => "ab🦀cdefg",
|
|
1415
|
-
_ => unreachable!(),
|
|
1416
|
-
};
|
|
1417
|
-
let line = Line::from(value).centered();
|
|
1418
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
|
1419
|
-
line.render(buf.area, &mut buf);
|
|
1420
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
/// Ensures the rendering also works away from the 0x0 position.
|
|
1424
|
-
///
|
|
1425
|
-
/// Particularly of note is that an emoji that is truncated will not overwrite the
|
|
1426
|
-
/// characters that are already in the buffer. This is inentional (consider how a line
|
|
1427
|
-
/// that is rendered on a border should not overwrite the border with a partial emoji).
|
|
1428
|
-
#[rstest]
|
|
1429
|
-
#[case::left(Alignment::Left, "XXa🦀bcXXX")]
|
|
1430
|
-
#[case::center(Alignment::Center, "XX🦀bc🦀XX")]
|
|
1431
|
-
#[case::right(Alignment::Right, "XXXbc🦀dXX")]
|
|
1432
|
-
fn render_truncates_away_from_0x0(#[case] alignment: Alignment, #[case] expected: &str) {
|
|
1433
|
-
let line = Line::from(vec![Span::raw("a🦀b"), Span::raw("c🦀d")]).alignment(alignment);
|
|
1434
|
-
// Fill buffer with stuff to ensure the output is indeed padded
|
|
1435
|
-
let mut buf = Buffer::filled(Rect::new(0, 0, 10, 1), Cell::new("X"));
|
|
1436
|
-
let area = Rect::new(2, 0, 6, 1);
|
|
1437
|
-
line.render(area, &mut buf);
|
|
1438
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
/// When two spans are rendered after each other the first needs to be padded in accordance
|
|
1442
|
-
/// to the skipped unicode width. In this case the first crab does not fit at width 6 which
|
|
1443
|
-
/// takes a front white space.
|
|
1444
|
-
#[rstest]
|
|
1445
|
-
#[case::right_4(4, "c🦀d")]
|
|
1446
|
-
#[case::right_5(5, "bc🦀d")]
|
|
1447
|
-
#[case::right_6(6, "Xbc🦀d")]
|
|
1448
|
-
#[case::right_7(7, "🦀bc🦀d")]
|
|
1449
|
-
#[case::right_8(8, "a🦀bc🦀d")]
|
|
1450
|
-
fn render_right_aligned_multi_span(#[case] buf_width: u16, #[case] expected: &str) {
|
|
1451
|
-
let line = Line::from(vec![Span::raw("a🦀b"), Span::raw("c🦀d")]).right_aligned();
|
|
1452
|
-
let area = Rect::new(0, 0, buf_width, 1);
|
|
1453
|
-
// Fill buffer with stuff to ensure the output is indeed padded
|
|
1454
|
-
let mut buf = Buffer::filled(area, Cell::new("X"));
|
|
1455
|
-
line.render(buf.area, &mut buf);
|
|
1456
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1460
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1461
|
-
///
|
|
1462
|
-
/// Flag emoji are actually two independent characters, so they can be truncated in the
|
|
1463
|
-
/// middle of the emoji. This test documents just the emoji part of the test.
|
|
1464
|
-
#[test]
|
|
1465
|
-
fn flag_emoji() {
|
|
1466
|
-
let str = "🇺🇸1234";
|
|
1467
|
-
assert_eq!(str.len(), 12); // flag is 4 bytes
|
|
1468
|
-
assert_eq!(str.chars().count(), 6); // flag is 2 chars
|
|
1469
|
-
assert_eq!(str.graphemes(true).count(), 5); // flag is 1 grapheme
|
|
1470
|
-
assert_eq!(str.width(), 6); // flag is 2 display width
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
/// Part of a regression test for <https://github.com/ratatui/ratatui/issues/1032> which
|
|
1474
|
-
/// found panics with truncating lines that contained multi-byte characters.
|
|
1475
|
-
#[rstest]
|
|
1476
|
-
#[case::flag_1(1, " ")]
|
|
1477
|
-
#[case::flag_2(2, "🇺🇸")]
|
|
1478
|
-
#[case::flag_3(3, "🇺🇸1")]
|
|
1479
|
-
#[case::flag_4(4, "🇺🇸12")]
|
|
1480
|
-
#[case::flag_5(5, "🇺🇸123")]
|
|
1481
|
-
#[case::flag_6(6, "🇺🇸1234")]
|
|
1482
|
-
#[case::flag_7(7, "🇺🇸1234 ")]
|
|
1483
|
-
fn render_truncates_flag(#[case] buf_width: u16, #[case] expected: &str) {
|
|
1484
|
-
let line = Line::from("🇺🇸1234");
|
|
1485
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
|
1486
|
-
line.render(buf.area, &mut buf);
|
|
1487
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
// Buffer width is `u16`. A line can be longer.
|
|
1491
|
-
#[rstest]
|
|
1492
|
-
#[case::left(Alignment::Left, "This is some content with a some")]
|
|
1493
|
-
#[case::right(Alignment::Right, "horribly long Line over u16::MAX")]
|
|
1494
|
-
fn render_truncates_very_long_line_of_many_spans(
|
|
1495
|
-
#[case] alignment: Alignment,
|
|
1496
|
-
#[case] expected: &str,
|
|
1497
|
-
) {
|
|
1498
|
-
let part = "This is some content with a somewhat long width to be repeated over and over again to create horribly long Line over u16::MAX";
|
|
1499
|
-
let min_width = usize::from(u16::MAX).saturating_add(1);
|
|
1500
|
-
|
|
1501
|
-
// width == len as only ASCII is used here
|
|
1502
|
-
let factor = min_width.div_ceil(part.len());
|
|
1503
|
-
|
|
1504
|
-
let line = Line::from(vec![Span::raw(part); factor]).alignment(alignment);
|
|
1505
|
-
|
|
1506
|
-
dbg!(line.width());
|
|
1507
|
-
assert!(line.width() >= min_width);
|
|
1508
|
-
|
|
1509
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 32, 1));
|
|
1510
|
-
line.render(buf.area, &mut buf);
|
|
1511
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
// Buffer width is `u16`. A single span inside a line can be longer.
|
|
1515
|
-
#[rstest]
|
|
1516
|
-
#[case::left(Alignment::Left, "This is some content with a some")]
|
|
1517
|
-
#[case::right(Alignment::Right, "horribly long Line over u16::MAX")]
|
|
1518
|
-
fn render_truncates_very_long_single_span_line(
|
|
1519
|
-
#[case] alignment: Alignment,
|
|
1520
|
-
#[case] expected: &str,
|
|
1521
|
-
) {
|
|
1522
|
-
let part = "This is some content with a somewhat long width to be repeated over and over again to create horribly long Line over u16::MAX";
|
|
1523
|
-
let min_width = usize::from(u16::MAX).saturating_add(1);
|
|
1524
|
-
|
|
1525
|
-
// width == len as only ASCII is used here
|
|
1526
|
-
let factor = min_width.div_ceil(part.len());
|
|
1527
|
-
|
|
1528
|
-
let line = Line::from(vec![Span::raw(part.repeat(factor))]).alignment(alignment);
|
|
1529
|
-
|
|
1530
|
-
dbg!(line.width());
|
|
1531
|
-
assert!(line.width() >= min_width);
|
|
1532
|
-
|
|
1533
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 32, 1));
|
|
1534
|
-
line.render(buf.area, &mut buf);
|
|
1535
|
-
assert_eq!(buf, Buffer::with_lines([expected]));
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
#[test]
|
|
1539
|
-
fn render_with_newlines() {
|
|
1540
|
-
let mut buf = Buffer::empty(Rect::new(0, 0, 11, 1));
|
|
1541
|
-
Line::from("Hello\nworld!").render(Rect::new(0, 0, 11, 1), &mut buf);
|
|
1542
|
-
assert_eq!(buf, Buffer::with_lines(["Helloworld!"]));
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
mod iterators {
|
|
1547
|
-
use super::*;
|
|
1548
|
-
|
|
1549
|
-
/// a fixture used in the tests below to avoid repeating the same setup
|
|
1550
|
-
#[fixture]
|
|
1551
|
-
fn hello_world() -> Line<'static> {
|
|
1552
|
-
Line::from(vec![
|
|
1553
|
-
Span::styled("Hello ", Color::Blue),
|
|
1554
|
-
Span::styled("world!", Color::Green),
|
|
1555
|
-
])
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
#[rstest]
|
|
1559
|
-
fn iter(hello_world: Line<'_>) {
|
|
1560
|
-
let mut iter = hello_world.iter();
|
|
1561
|
-
assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue)));
|
|
1562
|
-
assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green)));
|
|
1563
|
-
assert_eq!(iter.next(), None);
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
#[rstest]
|
|
1567
|
-
fn iter_mut(mut hello_world: Line<'_>) {
|
|
1568
|
-
let mut iter = hello_world.iter_mut();
|
|
1569
|
-
assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue)));
|
|
1570
|
-
assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green)));
|
|
1571
|
-
assert_eq!(iter.next(), None);
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
#[rstest]
|
|
1575
|
-
fn into_iter(hello_world: Line<'_>) {
|
|
1576
|
-
let mut iter = hello_world.into_iter();
|
|
1577
|
-
assert_eq!(iter.next(), Some(Span::styled("Hello ", Color::Blue)));
|
|
1578
|
-
assert_eq!(iter.next(), Some(Span::styled("world!", Color::Green)));
|
|
1579
|
-
assert_eq!(iter.next(), None);
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
#[rstest]
|
|
1583
|
-
fn into_iter_ref(hello_world: Line<'_>) {
|
|
1584
|
-
let mut iter = (&hello_world).into_iter();
|
|
1585
|
-
assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue)));
|
|
1586
|
-
assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green)));
|
|
1587
|
-
assert_eq!(iter.next(), None);
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
#[test]
|
|
1591
|
-
fn into_iter_mut_ref() {
|
|
1592
|
-
let mut hello_world = Line::from(vec![
|
|
1593
|
-
Span::styled("Hello ", Color::Blue),
|
|
1594
|
-
Span::styled("world!", Color::Green),
|
|
1595
|
-
]);
|
|
1596
|
-
let mut iter = (&mut hello_world).into_iter();
|
|
1597
|
-
assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue)));
|
|
1598
|
-
assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green)));
|
|
1599
|
-
assert_eq!(iter.next(), None);
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
#[rstest]
|
|
1603
|
-
fn for_loop_ref(hello_world: Line<'_>) {
|
|
1604
|
-
let mut result = String::new();
|
|
1605
|
-
for span in &hello_world {
|
|
1606
|
-
result.push_str(span.content.as_ref());
|
|
1607
|
-
}
|
|
1608
|
-
assert_eq!(result, "Hello world!");
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
#[rstest]
|
|
1612
|
-
fn for_loop_mut_ref() {
|
|
1613
|
-
let mut hello_world = Line::from(vec![
|
|
1614
|
-
Span::styled("Hello ", Color::Blue),
|
|
1615
|
-
Span::styled("world!", Color::Green),
|
|
1616
|
-
]);
|
|
1617
|
-
let mut result = String::new();
|
|
1618
|
-
for span in &mut hello_world {
|
|
1619
|
-
result.push_str(span.content.as_ref());
|
|
1620
|
-
}
|
|
1621
|
-
assert_eq!(result, "Hello world!");
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
#[rstest]
|
|
1625
|
-
fn for_loop_into(hello_world: Line<'_>) {
|
|
1626
|
-
let mut result = String::new();
|
|
1627
|
-
for span in hello_world {
|
|
1628
|
-
result.push_str(span.content.as_ref());
|
|
1629
|
-
}
|
|
1630
|
-
assert_eq!(result, "Hello world!");
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
#[rstest]
|
|
1635
|
-
#[case::empty(Line::default(), "Line::default()")]
|
|
1636
|
-
#[case::raw(Line::raw("Hello, world!"), r#"Line::from("Hello, world!")"#)]
|
|
1637
|
-
#[case::styled(
|
|
1638
|
-
Line::styled("Hello, world!", Color::Yellow),
|
|
1639
|
-
r#"Line::from("Hello, world!").yellow()"#
|
|
1640
|
-
)]
|
|
1641
|
-
#[case::styled_complex(
|
|
1642
|
-
Line::from(String::from("Hello, world!")).green().on_blue().bold().italic().not_dim(),
|
|
1643
|
-
r#"Line::from("Hello, world!").green().on_blue().bold().italic().not_dim()"#
|
|
1644
|
-
)]
|
|
1645
|
-
#[case::styled_span(
|
|
1646
|
-
Line::from(Span::styled("Hello, world!", Color::Yellow)),
|
|
1647
|
-
r#"Line::from(Span::from("Hello, world!").yellow())"#
|
|
1648
|
-
)]
|
|
1649
|
-
#[case::styled_line_and_span(
|
|
1650
|
-
Line::from(vec![
|
|
1651
|
-
Span::styled("Hello", Color::Yellow),
|
|
1652
|
-
Span::styled(" world!", Color::Green),
|
|
1653
|
-
]).italic(),
|
|
1654
|
-
r#"Line::from_iter([Span::from("Hello").yellow(), Span::from(" world!").green()]).italic()"#
|
|
1655
|
-
)]
|
|
1656
|
-
#[case::spans_vec(
|
|
1657
|
-
Line::from(vec![
|
|
1658
|
-
Span::styled("Hello", Color::Blue),
|
|
1659
|
-
Span::styled(" world!", Color::Green),
|
|
1660
|
-
]),
|
|
1661
|
-
r#"Line::from_iter([Span::from("Hello").blue(), Span::from(" world!").green()])"#,
|
|
1662
|
-
)]
|
|
1663
|
-
#[case::left_aligned(
|
|
1664
|
-
Line::from("Hello, world!").left_aligned(),
|
|
1665
|
-
r#"Line::from("Hello, world!").left_aligned()"#
|
|
1666
|
-
)]
|
|
1667
|
-
#[case::centered(
|
|
1668
|
-
Line::from("Hello, world!").centered(),
|
|
1669
|
-
r#"Line::from("Hello, world!").centered()"#
|
|
1670
|
-
)]
|
|
1671
|
-
#[case::right_aligned(
|
|
1672
|
-
Line::from("Hello, world!").right_aligned(),
|
|
1673
|
-
r#"Line::from("Hello, world!").right_aligned()"#
|
|
1674
|
-
)]
|
|
1675
|
-
fn debug(#[case] line: Line, #[case] expected: &str) {
|
|
1676
|
-
assert_eq!(format!("{line:?}"), expected);
|
|
1677
|
-
}
|
|
1678
|
-
}
|