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
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use tui::{
    style::Style,
    text::{Span, Spans, StyledGrapheme},
};

pub trait IntoSpan<'a> {
    fn into_span(self) -> Span<'a>;
}

impl<'a> IntoSpan<'a> for StyledGrapheme<'a> {
    fn into_span(self) -> Span<'a> {
        Span::styled(self.symbol, self.style)
    }
}

pub trait IntoSpans<'a> {
    fn into_spans(self) -> Spans<'a>;
}

impl<'a> IntoSpans<'a> for Vec<StyledGrapheme<'a>> {
    fn into_spans(self) -> Spans<'a> {
        self.into_iter()
            .fold(None, |mut acc: Option<(Vec<Span<'a>>, Style)>, x| {
                let x_style = x.style;
                match acc {
                    Some((ref mut vec, ref mut style)) => {
                        if style == &x_style {
                            vec.last_mut().expect("vec.len() >= 1").content += x.symbol;
                        } else {
                            vec.push(x.into_span());
                            *style = x_style
                        }
                    }
                    None => return Some((vec![x.into_span()], x_style)),
                };
                acc
            })
            .map(|(vec, _)| vec)
            .unwrap_or_default()
            .into()
    }
}

impl<'a> IntoSpans<'a> for Vec<(Style, char)> {
    fn into_spans(self) -> Spans<'a> {
        self.into_iter()
            .fold(
                None,
                |mut acc: Option<(Vec<(Style, String)>, Style)>, (x_style, c)| {
                    match acc {
                        Some((ref mut vec, ref mut style)) => {
                            if style == &x_style {
                                let last = &mut vec.last_mut().expect("vec.len() >= 1").1;
                                last.push(c);
                            } else {
                                vec.push((x_style, c.to_string()));
                                *style = x_style
                            }
                        }
                        None => return Some((vec![(x_style, c.to_string())], x_style)),
                    };
                    acc
                },
            )
            .map(|(vec, _)| {
                vec.into_iter()
                    .map(|(style, string)| Span::styled(string, style))
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default()
            .into()
    }
}

#[test]
fn test_into_span() {
    use tui::style::Color;

    let style_blue = Style::default().fg(Color::Blue);
    let style_plain = Style::default();
    let style_red = Style::default().fg(Color::Red);

    let (a, b, c) = (
        Span::raw("Hello"),
        Span::raw(" "),
        Span::raw("World 中文测试"),
    );
    let chars_blue = a.styled_graphemes(style_blue);
    let chars_plain = b.styled_graphemes(style_plain);
    let chars_red = c.styled_graphemes(style_red);

    let spans = chars_blue
        .chain(chars_plain)
        .chain(chars_red)
        .collect::<Vec<_>>()
        .into_spans();

    assert_eq!(
        spans,
        Spans::from(vec![
            Span {
                content: "Hello".into(),
                style: style_blue
            },
            Span {
                content: " ".into(),
                style: style_plain
            },
            Span {
                content: "World 中文测试".into(),
                style: style_red
            },
        ])
    )
}