From: Simon Tatham Date: Mon, 25 Dec 2023 21:16:03 +0000 (+0000) Subject: Re: header. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=0d6b7410777b0ad0adebbdfa2e5ab52b4203398a;p=mastodonochrome.git Re: header. --- diff --git a/src/text.rs b/src/text.rs index 9b59666..96067dc 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,5 +1,5 @@ use chrono::{DateTime, Utc, Local}; -use core::cmp::{max, min}; +use core::cmp::min; use std::collections::{HashMap, BTreeSet}; use super::html; @@ -15,7 +15,7 @@ pub trait TextFragment { fn as_extendable_indicator(&self) -> Option<&ExtendableIndicator> { None } } -pub struct BlankLine {} +struct BlankLine {} pub fn blank() -> Box { Box::new(BlankLine{}) @@ -36,7 +36,7 @@ fn test_blank() { }); } -pub struct SeparatorLine { +struct SeparatorLine { timestamp: Option>, favourited: bool, boosted: bool, @@ -101,7 +101,7 @@ fn test_separator() { }); } -pub struct EditorHeaderSeparator {} +struct EditorHeaderSeparator {} pub fn editorsep() -> Box { Box::new(EditorHeaderSeparator{}) @@ -111,7 +111,7 @@ impl TextFragment for EditorHeaderSeparator { fn render(&self, width: usize) -> Vec { vec! { ColouredString::uniform( - &((&"-".repeat(max(0, width - 2))).to_string() + "|"), + &((&"-".repeat(width - min(2, width))).to_string() + "|"), '-', ).truncate(width).to_owned(), } @@ -128,7 +128,7 @@ fn test_editorsep() { }); } -pub struct UsernameHeader { +struct UsernameHeader { header: String, colour: char, account: String, @@ -197,6 +197,10 @@ impl ColouredString { false } } + + fn is_colour(&self, colour: char) -> bool { + self.frags().all(|(_, c)| c == colour) + } } impl<'a> ColouredStringSlice<'a> { @@ -251,18 +255,20 @@ impl Paragraph { self } - pub fn push_para(mut self, para: &Paragraph) -> Self { - let tail = if let Some(last_existing_word) = self.words.last_mut() { - if let Some(first_new_word) = para.words.first() { - if last_existing_word.is_space() == - first_new_word.is_space() { - last_existing_word.push_str(&first_new_word.slice()); - 1 - } else { 0 } - } else { 0 } - } else { 0 }; - self.words.extend_from_slice(¶.words[tail..]); - self + pub fn push_para(&mut self, para: &Paragraph) { + if let Some(word) = self.words.last() { + if !word.is_space() { + self.push_text(&ColouredString::plain(" "), false); + } + } + self.words.extend_from_slice(¶.words); + } + + pub fn delete_mention_words_from(&mut self, start: usize) { + let first_non_mention = start + self.words[start..].iter() + .position(|word| !(word.is_space() || word.is_colour('@'))) + .unwrap_or(self.words.len() - start); + self.words.splice(start..first_non_mention, vec!{}); } pub fn into_box(self) -> Box { Box::new(self) } @@ -433,7 +439,7 @@ fn test_para_wrap() { }); } -pub struct FileHeader { +struct FileHeader { text: ColouredString, } @@ -775,8 +781,67 @@ fn test_extendable() { }); } +pub struct InReplyToLine { + para: Paragraph, +} + +pub fn in_reply_to_line(post: &Vec) -> Box { + let mut para = Paragraph::new().add(&ColouredString::plain("Re: ")); + let currlen = para.words.len(); + for cpara in post { + para.push_para(cpara); + } + para.delete_mention_words_from(currlen); + Box::new(InReplyToLine { + para: para + }) +} + +impl TextFragment for InReplyToLine { + fn render(&self, width: usize) -> Vec { + let rendered_para = self.para.render(width - min(width, 4)); + let mut it = rendered_para.iter(); + // "Re:" guarantees the first line must exist at least + let first_line = it.next().unwrap(); + let result = if it.next().is_some() { + first_line + ColouredString::plain("...") + } else { + first_line.clone() + }; + vec! { result.truncate(width).to_owned() } + } +} + +#[test] +fn test_in_reply_to() { + let post = vec! { + Paragraph::new().add(&ColouredString::general( + "@stoat @weasel take a look at this otter!", + "@@@@@@ @@@@@@@ ")), + Paragraph::new().add(&ColouredString::general( + "@badger might also like it", + "@@@@@@@ ")), + }; + + let irt = in_reply_to_line(&post); + assert_eq!(irt.render(48), vec!{ + ColouredString::general( + "Re: take a look at this otter! @badger might...", + " @@@@@@@ "), + }); + assert_eq!(irt.render(47), vec!{ + ColouredString::general( + "Re: take a look at this otter! @badger...", + " @@@@@@@ "), + }); + assert_eq!(irt.render(80), vec!{ + ColouredString::general( + "Re: take a look at this otter! @badger might also like it", + " @@@@@@@ "), + }); +} + // TODO: -// InReplyToLine, with first line of included paragraph // NotificationLog, also with included toot // UserListEntry // Media