pub fn truncate(&self, width: usize) -> ColouredStringSlice {
self.split(width).next().unwrap()
}
+
+ pub fn push_str(&mut self, more: &ColouredStringSlice<'_>) {
+ self.text.push_str(more.text);
+ self.colour.push_str(more.colour);
+ }
}
impl<'a> ColouredStringSlice<'a> {
}
}
+pub struct ColouredStringCharIterator<'a> {
+ cs: ColouredStringSlice<'a>,
+ textpos: usize,
+ colourpos: usize,
+}
+
pub struct ColouredStringFragIterator<'a> {
cs: ColouredStringSlice<'a>,
textpos: usize,
}
impl<'a> ColouredStringSlice<'a> {
+ pub fn chars(&self) -> ColouredStringCharIterator<'a> {
+ ColouredStringCharIterator {
+ cs: self.clone(),
+ textpos: 0,
+ colourpos: 0,
+ }
+ }
pub fn frags(&self) -> ColouredStringFragIterator<'a> {
ColouredStringFragIterator {
cs: self.clone(),
}
impl<'a> ColouredString {
+ pub fn chars(&'a self) -> ColouredStringCharIterator<'a> {
+ ColouredStringCharIterator {
+ cs: self.slice(),
+ textpos: 0,
+ colourpos: 0,
+ }
+ }
pub fn frags(&'a self) -> ColouredStringFragIterator<'a> {
ColouredStringFragIterator {
cs: self.slice(),
}
}
+impl<'a> Iterator for ColouredStringCharIterator<'a> {
+ type Item = ColouredStringSlice<'a>;
+ fn next(&mut self) -> Option<Self::Item> {
+ let textslice = &self.cs.text[self.textpos..];
+ let mut textit = textslice.char_indices();
+ let colourslice = &self.cs.colour[self.colourpos..];
+ let mut colourit = colourslice.char_indices();
+ if let (Some(_), Some(_)) = (textit.next(), colourit.next()) {
+ let (textend, colourend) = match (textit.next(), colourit.next()) {
+ (Some((tpos, _)), Some((cpos, _))) => (tpos, cpos),
+ _ => (textslice.len(), colourslice.len()),
+ };
+ self.textpos += textend;
+ self.colourpos += colourend;
+ Some(ColouredStringSlice {
+ text: &textslice[..textend],
+ colour: &colourslice[..colourend],
+ })
+ } else {
+ None
+ }
+ }
+}
+
impl<'a> Iterator for ColouredStringFragIterator<'a> {
type Item = (&'a str, char);
fn next(&mut self) -> Option<Self::Item> {
assert_eq!(ColouredString::general("xy\u{302}z", "pqqr").width(), 3);
}
+#[test]
+fn test_chars() {
+ let cs = ColouredString::general("ábé", "xyz");
+ let mut chars = cs.chars();
+ assert_eq!(chars.next(), Some(ColouredStringSlice::general("á", "x")));
+ assert_eq!(chars.next(), Some(ColouredStringSlice::general("b", "y")));
+ assert_eq!(chars.next(), Some(ColouredStringSlice::general("é", "z")));
+ assert_eq!(chars.next(), None);
+}
+
#[test]
fn test_frags() {
let cs = ColouredString::general("stóat wèasël", "uuuuu vvvvvv");
use chrono::{DateTime,Utc,Local};
use core::cmp::max;
-use super::coloured_string::ColouredString;
+use super::coloured_string::{ColouredString, ColouredStringSlice};
pub trait TextFragment {
// FIXME: we will also need ...
}
}
+#[test]
+fn test_blank() {
+ assert_eq!(blank().render(40), vec! {
+ ColouredString::plain("")
+ });
+}
+
pub struct SeparatorLine {
timestamp: Option<DateTime<Utc>>,
favourited: bool,
}
}
+#[test]
+fn test_separator() {
+ // Would be nice to test time formatting here, but I'd have to
+ // think of a way to test it independently of TZ
+ assert_eq!(separator(None, true, false)
+ .render(60), vec! {
+ ColouredString::general(
+ "------------------------------------------------------[F]--",
+ "SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSSS",
+ )
+ });
+}
+
pub struct EditorHeaderSeparator {}
pub fn editorsep() -> Box<dyn TextFragment> {
}
}
+#[test]
+fn test_editorsep() {
+ assert_eq!(editorsep().render(5), vec! {
+ ColouredString::general(
+ "---|",
+ "----",
+ )
+ });
+}
+
pub struct UsernameHeader {
header: String,
colour: char,
}
}
-#[test]
-fn test_blank() {
- assert_eq!(blank().render(40), vec! {
- ColouredString::plain("")
- });
-}
-
-#[test]
-fn test_separator() {
- // Would be nice to test time formatting here, but I'd have to
- // think of a way to test it independently of TZ
- assert_eq!(separator(None, true, false)
- .render(60), vec! {
- ColouredString::general(
- "------------------------------------------------------[F]--",
- "SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSSS",
- )
- });
-}
-
-#[test]
-fn test_editorsep() {
- assert_eq!(editorsep().render(5), vec! {
- ColouredString::general(
- "---|",
- "----",
- )
- });
-}
-
#[test]
fn test_userheader() {
assert_eq!(fromline("stoat@example.com", "Some Person").render(80), vec! {
)
});
}
+
+#[derive(PartialEq, Eq, Debug)]
+pub struct Paragraph {
+ words: Vec<ColouredString>,
+ firstindent: usize,
+ laterindent: usize,
+ wrap: bool,
+}
+
+impl ColouredString {
+ fn is_space(&self) -> bool {
+ if let Some(ch) = self.text().chars().next() {
+ ch == ' '
+ } else {
+ false
+ }
+ }
+}
+
+impl<'a> ColouredStringSlice<'a> {
+ fn is_space(&self) -> bool {
+ if let Some(ch) = self.text().chars().next() {
+ ch == ' '
+ } else {
+ false
+ }
+ }
+}
+
+impl Paragraph {
+ pub fn new() -> Self {
+ Paragraph {
+ words: Vec::new(),
+ firstindent: 0,
+ laterindent: 0,
+ wrap: true,
+ }
+ }
+
+ pub fn set_wrap(mut self, wrap: bool) -> Self {
+ self.wrap = wrap;
+ self
+ }
+
+ pub fn set_indent(mut self, firstindent: usize,
+ laterindent: usize) -> Self {
+ self.firstindent = firstindent;
+ self.laterindent = laterindent;
+ self
+ }
+
+ pub fn add(mut self, text: &ColouredString) -> Self {
+ for ch in text.chars() {
+ if let Some(curr_word) = self.words.last_mut() {
+ if ch.is_space() == curr_word.is_space() {
+ curr_word.push_str(&ch);
+ continue;
+ }
+ }
+ self.words.push(ch.to_owned());
+ }
+ self
+ }
+
+ pub fn into_box(self) -> Box<dyn TextFragment> { Box::new(self) }
+}
+
+#[test]
+fn test_para_build() {
+ assert_eq!(Paragraph::new(), Paragraph {
+ words: Vec::new(),
+ firstindent: 0,
+ laterindent: 0,
+ wrap: true,
+ });
+ assert_eq!(Paragraph::new().set_wrap(false), Paragraph {
+ words: Vec::new(),
+ firstindent: 0,
+ laterindent: 0,
+ wrap: false,
+ });
+ assert_eq!(Paragraph::new().set_indent(3, 4), Paragraph {
+ words: Vec::new(),
+ firstindent: 3,
+ laterindent: 4,
+ wrap: true,
+ });
+ assert_eq!(Paragraph::new().add(&ColouredString::plain("foo bar baz")),
+ Paragraph {
+ words: vec! {
+ ColouredString::plain("foo"),
+ ColouredString::plain(" "),
+ ColouredString::plain("bar"),
+ ColouredString::plain(" "),
+ ColouredString::plain("baz"),
+ },
+ firstindent: 0,
+ laterindent: 0,
+ wrap: true,
+ });
+ assert_eq!(Paragraph::new()
+ .add(&ColouredString::plain("foo ba"))
+ .add(&ColouredString::plain("r baz")),
+ Paragraph {
+ words: vec! {
+ ColouredString::plain("foo"),
+ ColouredString::plain(" "),
+ ColouredString::plain("bar"),
+ ColouredString::plain(" "),
+ ColouredString::plain("baz"),
+ },
+ firstindent: 0,
+ laterindent: 0,
+ wrap: true,
+ });
+ assert_eq!(Paragraph::new().add(&ColouredString::general(
+ " foo bar baz ", "abcdefghijklmnopq")),
+ Paragraph {
+ words: vec! {
+ ColouredString::general(" ", "ab"),
+ ColouredString::general("foo", "cde"),
+ ColouredString::general(" ", "fg"),
+ ColouredString::general("bar", "hij"),
+ ColouredString::general(" ", "kl"),
+ ColouredString::general("baz", "mno"),
+ ColouredString::general(" ", "pq"),
+ },
+ firstindent: 0,
+ laterindent: 0,
+ wrap: true,
+ });
+}
+
+impl TextFragment for Paragraph {
+ fn render(&self, _width: usize) -> Vec<ColouredString> {
+ vec! {
+ ColouredString::plain("FIXME"),
+ }
+ }
+}