mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-25 22:20:31 +00:00
feat(line): implement iterators for Line (#896)
This allows iterating over the `Span`s of a line using `for` loops and other iterator methods. - add `iter` and `iter_mut` methods to `Line` - implement `IntoIterator` for `Line`, `&Line`, and `&mut Line` traits - update call sites to iterate over `Line` rather than `Line::spans`
This commit is contained in:
parent
86168aa711
commit
4278b4088d
3 changed files with 130 additions and 4 deletions
|
@ -243,7 +243,7 @@ impl Buffer {
|
||||||
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) {
|
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) {
|
||||||
let mut remaining_width = width;
|
let mut remaining_width = width;
|
||||||
let mut x = x;
|
let mut x = x;
|
||||||
for span in &line.spans {
|
for span in line {
|
||||||
if remaining_width == 0 {
|
if remaining_width == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
129
src/text/line.rs
129
src/text/line.rs
|
@ -368,6 +368,43 @@ impl<'a> Line<'a> {
|
||||||
pub fn reset_style(self) -> Self {
|
pub fn reset_style(self) -> Self {
|
||||||
self.patch_style(Style::reset())
|
self.patch_style(Style::reset())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the spans of this line.
|
||||||
|
pub fn iter(&self) -> std::slice::Iter<Span<'a>> {
|
||||||
|
self.spans.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable iterator over the spans of this line.
|
||||||
|
pub fn iter_mut(&mut self) -> std::slice::IterMut<Span<'a>> {
|
||||||
|
self.spans.iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for Line<'a> {
|
||||||
|
type Item = Span<'a>;
|
||||||
|
type IntoIter = std::vec::IntoIter<Span<'a>>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.spans.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a Line<'a> {
|
||||||
|
type Item = &'a Span<'a>;
|
||||||
|
type IntoIter = std::slice::Iter<'a, Span<'a>>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a mut Line<'a> {
|
||||||
|
type Item = &'a mut Span<'a>;
|
||||||
|
type IntoIter = std::slice::IterMut<'a, Span<'a>>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<String> for Line<'a> {
|
impl<'a> From<String> for Line<'a> {
|
||||||
|
@ -399,7 +436,7 @@ impl<'a> From<Span<'a>> for Line<'a> {
|
||||||
|
|
||||||
impl<'a> From<Line<'a>> for String {
|
impl<'a> From<Line<'a>> for String {
|
||||||
fn from(line: Line<'a>) -> String {
|
fn from(line: Line<'a>) -> String {
|
||||||
line.spans.iter().fold(String::new(), |mut acc, s| {
|
line.iter().fold(String::new(), |mut acc, s| {
|
||||||
acc.push_str(s.content.as_ref());
|
acc.push_str(s.content.as_ref());
|
||||||
acc
|
acc
|
||||||
})
|
})
|
||||||
|
@ -461,6 +498,8 @@ impl std::fmt::Display for Line<'_> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use rstest::{fixture, rstest};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -752,4 +791,92 @@ mod tests {
|
||||||
let line = Line::from("Hello, world!").right_aligned();
|
let line = Line::from("Hello, world!").right_aligned();
|
||||||
assert_eq!(line.alignment, Some(Alignment::Right));
|
assert_eq!(line.alignment, Some(Alignment::Right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod iterators {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// a fixture used in the tests below to avoid repeating the same setup
|
||||||
|
#[fixture]
|
||||||
|
fn hello_world() -> Line<'static> {
|
||||||
|
Line::from(vec![
|
||||||
|
Span::styled("Hello ", Color::Blue),
|
||||||
|
Span::styled("world!", Color::Green),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn iter(hello_world: Line<'_>) {
|
||||||
|
let mut iter = hello_world.iter();
|
||||||
|
assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue)));
|
||||||
|
assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn iter_mut(mut hello_world: Line<'_>) {
|
||||||
|
let mut iter = hello_world.iter_mut();
|
||||||
|
assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue)));
|
||||||
|
assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn into_iter(hello_world: Line<'_>) {
|
||||||
|
let mut iter = hello_world.into_iter();
|
||||||
|
assert_eq!(iter.next(), Some(Span::styled("Hello ", Color::Blue)));
|
||||||
|
assert_eq!(iter.next(), Some(Span::styled("world!", Color::Green)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn into_iter_ref(hello_world: Line<'_>) {
|
||||||
|
let mut iter = (&hello_world).into_iter();
|
||||||
|
assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue)));
|
||||||
|
assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn into_iter_mut_ref() {
|
||||||
|
let mut hello_world = Line::from(vec![
|
||||||
|
Span::styled("Hello ", Color::Blue),
|
||||||
|
Span::styled("world!", Color::Green),
|
||||||
|
]);
|
||||||
|
let mut iter = (&mut hello_world).into_iter();
|
||||||
|
assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue)));
|
||||||
|
assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn for_loop_ref(hello_world: Line<'_>) {
|
||||||
|
let mut result = String::new();
|
||||||
|
for span in &hello_world {
|
||||||
|
result.push_str(span.content.as_ref());
|
||||||
|
}
|
||||||
|
assert_eq!(result, "Hello world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn for_loop_mut_ref() {
|
||||||
|
let mut hello_world = Line::from(vec![
|
||||||
|
Span::styled("Hello ", Color::Blue),
|
||||||
|
Span::styled("world!", Color::Green),
|
||||||
|
]);
|
||||||
|
let mut result = String::new();
|
||||||
|
for span in &mut hello_world {
|
||||||
|
result.push_str(span.content.as_ref());
|
||||||
|
}
|
||||||
|
assert_eq!(result, "Hello world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn for_loop_into(hello_world: Line<'_>) {
|
||||||
|
let mut result = String::new();
|
||||||
|
for span in hello_world {
|
||||||
|
result.push_str(span.content.as_ref());
|
||||||
|
}
|
||||||
|
assert_eq!(result, "Hello world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,8 +357,7 @@ mod test {
|
||||||
let text = text.into();
|
let text = text.into();
|
||||||
let styled_lines = text.lines.iter().map(|line| {
|
let styled_lines = text.lines.iter().map(|line| {
|
||||||
(
|
(
|
||||||
line.spans
|
line.iter()
|
||||||
.iter()
|
|
||||||
.flat_map(|span| span.styled_graphemes(Style::default())),
|
.flat_map(|span| span.styled_graphemes(Style::default())),
|
||||||
line.alignment.unwrap_or(Alignment::Left),
|
line.alignment.unwrap_or(Alignment::Left),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue