Add '> ' prefix before each line of a blockquote, even after line-wrapping

This commit is contained in:
Val Lorentz 2023-11-26 12:21:13 +01:00
parent 61f30cfbf3
commit 13965a7e67
8 changed files with 344 additions and 131 deletions

View File

@ -89,7 +89,7 @@ impl Buffer for LogBuffer {
.iter()
.rev()
.map(|(line_id, line, prerender)| BufferItem {
content: BufferItemContent::Text(line.clone().into_text().unwrap_or_else(|e| {
content: BufferItemContent::SimpleText(line.clone().into_text().unwrap_or_else(|e| {
tracing::error!("Could not convert line from ANSI codes to ratatui: {}", e);
Text::raw(line)
})),

View File

@ -22,6 +22,7 @@ use futures::StreamExt;
use matrix_sdk::async_trait;
use nonempty::NonEmpty;
use ratatui::text::Text;
use smallvec::SmallVec;
use sorted_vec::SortedVec;
use crate::widgets::Prerender;
@ -92,7 +93,9 @@ impl PartialOrd for BufferSortKey {
#[derive(Debug, Clone)]
pub enum BufferItemContent<'buf> {
Text(Text<'buf>),
SimpleText(Text<'buf>),
/// Pairs of `(padding, content)`
Text(Vec<(String, Text<'buf>)>),
Divider(Text<'buf>),
Empty,
}

View File

@ -58,11 +58,17 @@ use crate::widgets::Prerender;
/// Like [`BufferItemContent`] but owned.
#[derive(Debug, Clone)]
pub enum OwnedBufferItemContent {
Text {
SimpleText {
event_id: Option<OwnedEventId>,
is_message: bool,
text: Text<'static>,
},
Text {
event_id: Option<OwnedEventId>,
is_message: bool,
/// `(padding, content)` pairs
text: Vec<(String, Text<'static>)>,
},
Divider(String),
Empty,
}
@ -71,19 +77,32 @@ impl DynamicUsage for OwnedBufferItemContent {
fn dynamic_usage(&self) -> usize {
std::mem::size_of::<Self>()
+ match self {
OwnedBufferItemContent::Text { text, .. } => {
OwnedBufferItemContent::SimpleText { text, .. } => {
text.width() * text.height() * 4 // FIXME: rough approx
},
OwnedBufferItemContent::Text { text, .. } => {
text
.iter()
.map(|item| item.1.width() * item.1.height() * 4)
.sum() // FIXME: rough approx
},
OwnedBufferItemContent::Divider(s) => s.dynamic_usage(),
OwnedBufferItemContent::Empty => 0,
}
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let (min, max) = match self {
OwnedBufferItemContent::Text { text, .. } => {
OwnedBufferItemContent::SimpleText { text, .. } => {
let area = text.width() * text.height();
(area, Some(area * 12)) // FIXME: rough approx
},
OwnedBufferItemContent::Text { text, .. } => {
let area = text
.iter()
.map(|item| item.1.width() * item.1.height())
.sum();
(area, Some(area * 12)) // FIXME: rough approx
},
OwnedBufferItemContent::Divider(s) => s.dynamic_usage_bounds(),
OwnedBufferItemContent::Empty => (0, Some(0)),
};
@ -175,22 +194,22 @@ impl SingleClientRoomBuffer {
// Like `format!()` but returns OwnedBufferItemContent::Text, with is_message=false
macro_rules! text {
($($tokens:tt)*) => {
($prefix: expr, $($tokens:tt)*) => {
OwnedBufferItemContent::Text {
event_id: event.event_id().map(ToOwned::to_owned),
is_message: false,
text: format_html(&self.config, &format!($($tokens)*))
text: format_html(&self.config, $prefix, &format!($($tokens)*))
}
}
}
// Like `format!()` but returns OwnedBufferItemContent::Text, with is_message=true
macro_rules! msg {
($($tokens:tt)*) => {
($prefix: expr, $($tokens:tt)*) => {
OwnedBufferItemContent::Text {
event_id: event.event_id().map(ToOwned::to_owned),
is_message: true,
text: format_html(&self.config, &format!($($tokens)*))
text: format_html(&self.config, $prefix, &format!($($tokens)*))
}
}
}
@ -217,73 +236,95 @@ impl SingleClientRoomBuffer {
"FormattedBody::sanitize_html set type to {:?} instead of Html",
formatted.format
);
msg!(" &lt;{}> {}", sender, formatted.body)
msg!(" ", "&lt;{}> {}", sender, formatted.body)
},
MessageType::Text(TextMessageEventContent { body, .. }) => {
msg!(" &lt;{}> {}", sender, escape_html(body))
msg!(" ", "&lt;{}> {}", sender, escape_html(body))
},
_ =>
// Fallback to text body
{
msg!(
" &lt;{}> {}",
" ",
"&lt;{}> {}",
sender,
escape_html(&message.body().replace('\n', "\n "))
)
},
},
RedactedMessage => msg!("xx &lt;{}> [redacted]", sender),
RedactedMessage => msg!("xx ", "&lt;{}> [redacted]", sender),
Sticker(sticker) => msg!(
"st &lt;{}> {}",
"st ",
"&lt;{}> {}",
sender,
escape_html(&sticker.content().body)
),
UnableToDecrypt(_) => text!("xx &lt;{}> [unable to decrypt]", sender),
UnableToDecrypt(_) => text!("xx ", "&lt;{}> [unable to decrypt]", sender),
MembershipChange(change) => {
use matrix_sdk_ui::timeline::MembershipChange::*;
if change.user_id() == event.sender() {
let Some(change_kind) = change.change() else {
return text!("--- {} made incomprehensible changes to themselves", sender);
return text!(
"--- ",
"{} made incomprehensible changes to themselves",
sender
);
};
match change_kind {
None => text!("--- {} made no discernable changes to themselves", sender),
Error => text!(
"xxx {} made a change to themselves that made matrix-sdk-ui error",
None => text!(
"--- ",
"{} made no discernable changes to themselves",
sender
),
Joined => text!("--> {} joined", sender),
Left => text!("&lt;-- {} left", sender),
Banned => text!("-x- {} banned themselves", sender),
Unbanned => text!("-x- {} unbanned themselves", sender),
Kicked => text!("&lt;!- {} kicked themselves", sender),
Invited => text!("-o- {} invited themselves", sender),
KickedAndBanned => text!("&lt;!x {} kicked and banned themselves", sender),
InvitationAccepted => text!("-o> {} accepted an invite", sender),
InvitationRejected => text!("-ox {} rejected an invite", sender),
InvitationRevoked => text!("--x {} revoked an invite", sender),
Knocked => text!("-?> {} knocked", sender),
KnockAccepted => text!("-?o {} accepted a knock", sender),
KnockRetracted => text!("-?x {} retracted a knock", sender),
KnockDenied => text!("-?x {} denied a knock", sender),
Error => text!(
"xxx ",
"{} made a change to themselves that made matrix-sdk-ui error",
sender
),
Joined => text!("--> ", "{} joined", sender),
Left => text!("&lt;-- ", "{} left", sender),
Banned => text!("-x- ", "{} banned themselves", sender),
Unbanned => text!("-x- ", "{} unbanned themselves", sender),
Kicked => text!("&lt;!- ", "{} kicked themselves", sender),
Invited => text!("-o- ", "{} invited themselves", sender),
KickedAndBanned => text!("&lt;!x ", "{} kicked and banned themselves", sender),
InvitationAccepted => text!("-o> ", "{} accepted an invite", sender),
InvitationRejected => text!("-ox ", "{} rejected an invite", sender),
InvitationRevoked => text!("--x ", "{} revoked an invite", sender),
Knocked => text!("-?> ", "{} knocked", sender),
KnockAccepted => text!("-?o ", "{} accepted a knock", sender),
KnockRetracted => text!("-?x ", "{} retracted a knock", sender),
KnockDenied => text!("-?x ", "{} denied a knock", sender),
NotImplemented => text!(
"xxx {} made a change matrix-sdk-ui does not support yet",
"xxx ",
"{} made a change matrix-sdk-ui does not support yet",
sender
),
}
} else if change.user_id() == "" {
let Some(change_kind) = change.change() else {
return text!("--- {} made incomprehensible changes", sender);
return text!("--- ", "{} made incomprehensible changes", sender);
};
match change_kind {
None => text!("--- {} made no discernable changes", sender),
Error => text!("xxx {} made a change that made matrix-sdk-ui error", sender),
None => text!("--- ", "{} made no discernable changes", sender),
Error => text!(
"xxx ",
"{} made a change that made matrix-sdk-ui error",
sender
),
Joined | Left | Banned | Unbanned | Kicked | Invited | KickedAndBanned
| InvitationAccepted | InvitationRejected | InvitationRevoked | Knocked
| KnockAccepted | KnockRetracted | KnockDenied => {
text!("--> {} made a non-sensical change: {:?}", sender, change)
text!(
"--> ",
"{} made a non-sensical change: {:?}",
sender,
change
)
},
NotImplemented => text!(
"xxx {} made a change matrix-sdk-ui does not support yet",
"xxx ",
"{} made a change matrix-sdk-ui does not support yet",
sender
),
}
@ -297,35 +338,48 @@ impl SingleClientRoomBuffer {
);
let target = markup_colored_by_mxid(&target, &target);
let Some(change_kind) = change.change() else {
return text!("--- {} made incomprehensible changes to {}", sender, target);
return text!(
"--- ",
"{} made incomprehensible changes to {}",
sender,
target
);
};
match change_kind {
None => text!("--- {} made no discernable changes to {}", sender, target),
None => text!(
"--- ",
"{} made no discernable changes to {}",
sender,
target
),
Error => text!(
"xxx {} made a change to {} that made matrix-sdk-ui error",
"xxx ",
"{} made a change to {} that made matrix-sdk-ui error",
sender,
target
),
Joined | Left => text!(
"--> {} made a non-sensical change to {}: {:?}",
"--> ",
"{} made a non-sensical change to {}: {:?}",
sender,
target,
change
),
Banned => text!("-x- {} banned {}", sender, target),
Unbanned => text!("-x- {} unbanned {}", sender, target),
Kicked => text!("&lt;!- {} kicked {}", sender, target),
Invited => text!("-o- {} invited {}", sender, target),
KickedAndBanned => text!("&lt;!x {} kicked and banned {}", sender, target),
InvitationAccepted => text!("-o> {} accepted an invite to {}", sender, target),
InvitationRejected => text!("-ox {} rejected an invite to {}", sender, target),
InvitationRevoked => text!("--x {} revoked an invite to {}", sender, target),
Knocked => text!("-?> {} made {} knock", sender, target),
KnockAccepted => text!("-?o {} accepted {}'s knock", sender, target),
KnockRetracted => text!("-?x {} retracted {}'s knock", sender, target),
KnockDenied => text!("-?x {} denied {}'s knock", sender, target),
Banned => text!("-x- ", "{} banned {}", sender, target),
Unbanned => text!("-x- ", "{} unbanned {}", sender, target),
Kicked => text!("&lt;!- ", "{} kicked {}", sender, target),
Invited => text!("-o- ", "{} invited {}", sender, target),
KickedAndBanned => text!("&lt;!x ", "{} kicked and banned {}", sender, target),
InvitationAccepted => text!("-o> ", "{} accepted an invite to {}", sender, target),
InvitationRejected => text!("-ox ", "{} rejected an invite to {}", sender, target),
InvitationRevoked => text!("--x ", "{} revoked an invite to {}", sender, target),
Knocked => text!("-?> ", "{} made {} knock", sender, target),
KnockAccepted => text!("-?o ", "{} accepted {}'s knock", sender, target),
KnockRetracted => text!("-?x ", "{} retracted {}'s knock", sender, target),
KnockDenied => text!("-?x ", "{} denied {}'s knock", sender, target),
NotImplemented => text!(
"xxx {} made a change to {} that matrix-sdk-ui does not support yet",
"xxx ",
"{} made a change to {} that matrix-sdk-ui does not support yet",
sender,
target
),
@ -333,13 +387,14 @@ impl SingleClientRoomBuffer {
}
},
ProfileChange(_) => text!("--- {} updated their profile", sender),
ProfileChange(_) => text!("--- ", "{} updated their profile", sender),
OtherState(state) => {
if state.state_key() == "" {
text!("--- {} changed the room: {:?}", sender, state.content())
text!("--- ", "{} changed the room: {:?}", sender, state.content())
} else {
text!(
"--- {} changed {}: {:?}",
"--- ",
"{} changed {}: {:?}",
sender,
escape_html(state.state_key()),
state.content() // TODO: escape html
@ -347,7 +402,8 @@ impl SingleClientRoomBuffer {
}
},
FailedToParseMessageLike { event_type, error } => text!(
"xxx {} sent a {} message that made matrix-sdk-ui error: {:?}",
"xxx ",
"{} sent a {} message that made matrix-sdk-ui error: {:?}",
sender,
escape_html(&event_type.to_string()),
error // TODO: escape html
@ -358,14 +414,15 @@ impl SingleClientRoomBuffer {
error,
} => {
text!(
"xxx {} made a {} change to {} that made matrix-sdk-ui error: {:?}",
"xxx ",
"{} made a {} change to {} that made matrix-sdk-ui error: {:?}",
sender,
escape_html(&event_type.to_string()),
escape_html(state_key),
error // TODO: escape html
)
},
Poll(_) => text!("-?- {} acted on a poll", sender),
Poll(_) => text!("-?- ", "{} acted on a poll", sender),
}
},
TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker) => {
@ -492,7 +549,7 @@ impl RoomBuffer {
.map(|item| {
(
None,
OwnedBufferItemContent::Text {
OwnedBufferItemContent::SimpleText {
event_id: None,
is_message: false,
text: Text::raw(format!("Initial item: {:#?}", item)),
@ -638,6 +695,9 @@ impl Buffer for RoomBuffer {
.rev()
.map(|(line_id, line, prerender)| BufferItem {
content: match line {
OwnedBufferItemContent::SimpleText { text, .. } => {
BufferItemContent::SimpleText(text.clone())
},
OwnedBufferItemContent::Text { text, .. } => BufferItemContent::Text(text.clone()),
OwnedBufferItemContent::Divider(text) => BufferItemContent::Divider(Text::raw(text)),
OwnedBufferItemContent::Empty => BufferItemContent::Empty,

View File

@ -26,7 +26,8 @@ use crate::components::Action;
use crate::config::{Config, ScrollAmount};
use crate::widgets::prerender::{PrerenderInner, PrerenderValue};
use crate::widgets::{
BacklogItemWidget, BottomAlignedParagraph, Divider, EmptyWidget, OverlappableWidget,
BacklogItemWidget, BottomAlignedContainer, BottomAlignedParagraph, Divider, EmptyWidget,
OverlappableWidget,
};
use super::Component;
@ -128,7 +129,17 @@ impl Backlog {
fn build_widget<'a>(&self, content: BufferItemContent<'a>, scroll: u64) -> BacklogItemWidget<'a> {
match content {
BufferItemContent::Text(text) => BottomAlignedParagraph::new(text).scroll(scroll).into(),
BufferItemContent::SimpleText(text) => {
BottomAlignedParagraph::new(text).scroll(scroll).into()
},
BufferItemContent::Text(text) => BottomAlignedContainer::new(
text
.into_iter()
.map(|(padding, text)| (padding, BottomAlignedParagraph::new(text)))
.collect(),
)
.scroll(scroll)
.into(),
BufferItemContent::Divider(text) => {
if scroll == 0 {
Divider::new(Paragraph::new(text).alignment(Alignment::Center)).into()

View File

@ -95,23 +95,25 @@ fn format_tree(
config: &Config,
tree: Rc<Node>,
state: &mut FormatState,
text: &mut Text<'static>,
text: &mut Vec<(String, Text<'static>)>,
mut previous_sibling_is_block: bool,
) -> bool {
use markup5ever_rcdom::NodeData::*;
let mut state = match &tree.data {
Document | Doctype { .. } | Comment { .. } | ProcessingInstruction { .. } => state.to_owned(),
Text { contents } => {
NodeData::Document
| NodeData::Doctype { .. }
| NodeData::Comment { .. }
| NodeData::ProcessingInstruction { .. } => state.to_owned(),
NodeData::Text { contents } => {
let s: String = contents.clone().into_inner().into();
let s = s.replace('\n', ""); // Lines are insignificant in HTML
if previous_sibling_is_block && !s.is_empty() {
text.lines.push(Line {
spans: vec![Span::styled(state.padding.to_owned(), state.style)],
alignment: None,
});
text.push((state.padding.clone(), Text::raw("")));
previous_sibling_is_block = false;
}
text
.last_mut()
.unwrap()
.1
.lines
.last_mut()
.unwrap()
@ -119,7 +121,7 @@ fn format_tree(
.push(Span::styled(s, state.style));
state.to_owned()
},
Element {
NodeData::Element {
name: QualName {
ns: ns!(html),
local: name,
@ -173,7 +175,7 @@ fn format_tree(
state
},
local_name!("br") => {
text.lines.push(Line::raw(state.padding.to_owned()));
text.push((state.padding.clone(), Text::raw("")));
state.to_owned()
},
local_name!("p") => {
@ -202,7 +204,7 @@ fn format_tree(
.borrow()
.iter()
.map(|child| match &child.data {
Element {
NodeData::Element {
name:
QualName {
ns: ns!(html),
@ -228,11 +230,8 @@ fn format_tree(
ListState::None => state.to_owned(),
ListState::Unordered => {
let mut line = Line::default();
line
.spans
.push(Span::styled(state.padding.to_owned(), state.style));
line.spans.push(Span::styled("* ", state.style));
text.lines.push(line);
text.push((state.padding.to_owned(), line.into()));
FormatState {
padding: state.padding.to_owned() + " ",
..state.to_owned()
@ -243,9 +242,6 @@ fn format_tree(
digits,
} => {
let mut line = Line::default();
line
.spans
.push(Span::styled(state.padding.to_owned(), state.style));
*counter += 1;
line
.spans
@ -256,7 +252,7 @@ fn format_tree(
state.style,
));
}
text.lines.push(line);
text.push((state.padding.to_owned(), line.into()));
FormatState {
padding: state.padding.to_owned() + " " + &" ".repeat(digits.into()),
..state.to_owned()
@ -278,7 +274,7 @@ fn format_tree(
},
_ => state.to_owned(),
},
Element { .. } => state.to_owned(), // Element not in the HTML namespace
NodeData::Element { .. } => state.to_owned(), // Element not in the HTML namespace
};
for subtree in tree.children.borrow().iter() {
@ -292,9 +288,12 @@ fn format_tree(
}
match &tree.data {
Document | Doctype { .. } | Comment { .. } | ProcessingInstruction { .. } => {},
Text { .. } => {},
Element {
NodeData::Document
| NodeData::Doctype { .. }
| NodeData::Comment { .. }
| NodeData::ProcessingInstruction { .. } => {},
NodeData::Text { .. } => {},
NodeData::Element {
name: QualName {
ns: ns!(html),
local: name,
@ -308,13 +307,16 @@ fn format_tree(
},
_ => {},
},
Element { .. } => {},
NodeData::Element { .. } => {},
}
previous_sibling_is_block
}
pub fn format_html(config: &Config, s: &str) -> Text<'static> {
/// Returns a list of pairs `(padding, text)`.
///
/// When rendering, the padding should be added left of every line of the text.
pub fn format_html(config: &Config, prefix: &'static str, s: &str) -> Vec<(String, Text<'static>)> {
let tree = parse_fragment(
RcDom::default(),
Default::default(),
@ -323,11 +325,12 @@ pub fn format_html(config: &Config, s: &str) -> Text<'static> {
)
.one(s)
.document;
let prefix = Text::raw(prefix);
let mut state = FormatState {
padding: " ".to_owned(),
padding: " ".repeat(prefix.width() + 1), // TODO: make +1 configurable
..Default::default()
};
let mut text = Text::raw("");
let mut text = vec![("".to_owned(), prefix)];
format_tree(config, tree, &mut state, &mut text, false);
text
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2023 Valentin Lorentz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use ratatui::prelude::*;
use ratatui::widgets::{Paragraph, Widget};
use super::{BottomAlignedParagraph, OverlappableWidget};
/// Container of multiple [`BottomAlignedParagraph`]
#[derive(Debug)]
pub struct BottomAlignedContainer<'a> {
/// Pairs of `(padding, content)`
paragraphs: Vec<(String, BottomAlignedParagraph<'a>)>,
/// Number of lines at the bottom that should not be rendered
scroll: u64,
}
impl<'a> BottomAlignedContainer<'a> {
pub fn new(paragraphs: Vec<(String, BottomAlignedParagraph<'a>)>) -> BottomAlignedContainer<'a> {
BottomAlignedContainer {
paragraphs,
scroll: 0,
}
}
/// How many lines should be skipped at the bottom
///
/// This is like [`Paragraph::scroll`](ratatui::widgets::Paragraph::scroll), but it's only vertical.
pub fn scroll(mut self, offset: u64) -> BottomAlignedContainer<'a> {
self.scroll = offset;
self
}
fn padding_width(padding: &Text<'_>, max_width: u16) -> u16 {
usize::min(
(max_width - 10).into(), // TODO: make the minimum content width (10) configurable
padding.width(),
) as u16
}
}
impl<'a> OverlappableWidget for BottomAlignedContainer<'a> {
fn height(&self, width: u16) -> u64 {
self
.paragraphs
.iter()
.map(|(padding, paragraph)| {
paragraph.height(width - Self::padding_width(&Line::raw(padding).into(), width))
})
.sum()
}
fn render_overlap(self, mut area: Rect, buf: &mut Buffer) -> (u16, u16) {
if area.height == 0 {
// Don't even bother
return (0, 0);
}
let mut scroll = self.scroll;
let mut actual_width = 0u16;
let mut actual_height = 0usize;
for (padding, paragraph) in self.paragraphs.into_iter().rev() {
let padding: Text<'_> = Line::raw(&padding).into();
let padding_width = Self::padding_width(&padding, area.width);
assert_eq!(
padding.height(),
1,
"Unexpected padding height: {} (padding={:?})",
padding.height(),
padding
);
// FIXME: paragraph.height() is expensive because it needs to run line-wrapping,
// and paragraph.render_overlap then needs to run it again twice.
let paragraph_height = paragraph.height(area.width - padding_width);
if paragraph_height <= scroll {
// Paragraph is under the viewport, don't render it
scroll -= paragraph_height;
} else {
let paragraph = paragraph.scroll(scroll);
let (actual_paragraph_width, actual_paragraph_height) = paragraph.render_overlap(
Rect {
x: area.x + padding_width,
width: area.width - padding_width,
..area
},
buf,
);
scroll = 0;
// Write the padding on each line the paragraph was rendered on
for y in (u16::max(
area.top(),
area.bottom().saturating_sub(actual_paragraph_height),
))..area.bottom()
{
Paragraph::new(padding.clone()).render(
Rect {
x: area.x,
y,
height: 1,
width: padding_width,
},
buf,
);
}
area.height = area.height.saturating_sub(actual_paragraph_height);
actual_height = actual_height.saturating_add(actual_paragraph_height.into());
actual_width = u16::max(actual_width, padding_width + actual_paragraph_width);
if area.height == 0 {
break;
}
}
}
(actual_width, actual_height as u16)
}
}

View File

@ -18,6 +18,8 @@ use enum_dispatch::enum_dispatch;
use ratatui::prelude::*;
use ratatui::widgets::Widget;
mod bottom_aligned_container;
pub use bottom_aligned_container::BottomAlignedContainer;
mod bottom_aligned_paragraph;
pub use bottom_aligned_paragraph::BottomAlignedParagraph;
@ -47,6 +49,7 @@ pub trait OverlappableWidget {
#[enum_dispatch(OverlappableWidget)]
pub enum BacklogItemWidget<'a> {
Paragraph(BottomAlignedParagraph<'a>),
Container(BottomAlignedContainer<'a>),
Divider(Divider<'a>),
Empty(EmptyWidget),
}

View File

@ -64,7 +64,7 @@ fn test_single_item() {
let mut bl = Backlog::new(config());
let prerender = Prerender::new();
let item = BufferItem {
content: BufferItemContent::Text(Text::raw("hello")),
content: BufferItemContent::SimpleText(Text::raw("hello")),
prerender: &prerender,
unique_id: None,
};
@ -91,7 +91,7 @@ fn test_single_item_cached() {
let mut bl = Backlog::new(config());
let prerender = Prerender::new();
let item = BufferItem {
content: BufferItemContent::Text(Text::raw("hello")),
content: BufferItemContent::SimpleText(Text::raw("hello")),
prerender: &prerender,
unique_id: None,
};
@ -116,7 +116,7 @@ fn test_single_item_cached() {
assert_eq!(prerender.key(), Some(10));
let item = BufferItem {
content: BufferItemContent::Text(Text::raw("hello")),
content: BufferItemContent::SimpleText(Text::raw("hello")),
prerender: &prerender,
unique_id: None,
};
@ -134,12 +134,12 @@ fn test_only_necessary_width() {
let prerender1 = Prerender::new();
let prerender2 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi\nworld")),
content: BufferItemContent::SimpleText(Text::raw("hi\nworld")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw(":)")),
content: BufferItemContent::SimpleText(Text::raw(":)")),
prerender: &prerender2,
unique_id: None,
};
@ -165,12 +165,12 @@ fn test_only_necessary_width() {
assert_eq!(prerender1.key(), Some(10));
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi\nworld")),
content: BufferItemContent::SimpleText(Text::raw("hi\nworld")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw(":)")),
content: BufferItemContent::SimpleText(Text::raw(":)")),
prerender: &prerender2,
unique_id: None,
};
@ -195,7 +195,7 @@ fn test_single_item_tight() {
let mut bl = Backlog::new(config());
let prerender = Prerender::new();
let item = BufferItem {
content: BufferItemContent::Text(Text::raw("hello")),
content: BufferItemContent::SimpleText(Text::raw("hello")),
prerender: &prerender,
unique_id: None,
};
@ -221,13 +221,13 @@ fn test_two_items() {
let mut bl = Backlog::new(config());
let prerender1 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: None,
};
let prerender2 = Prerender::new();
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: None,
};
@ -255,12 +255,12 @@ fn test_two_items_scroll() {
let prerender2 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: Some(123),
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: Some(456),
};
@ -283,12 +283,12 @@ fn test_two_items_scroll() {
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: Some(123),
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: Some(456),
};
@ -311,12 +311,12 @@ fn test_two_items_scroll() {
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: Some(123),
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: Some(456),
};
@ -342,13 +342,13 @@ fn test_two_items_multiline() {
let mut bl = Backlog::new(config());
let prerender1 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: None,
};
let prerender2 = Prerender::new();
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world\n!")),
content: BufferItemContent::SimpleText(Text::raw("world\n!")),
prerender: &prerender2,
unique_id: None,
};
@ -374,13 +374,13 @@ fn test_two_items_tight() {
let mut bl = Backlog::new(config());
let prerender1 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: None,
};
let prerender2 = Prerender::new();
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: None,
};
@ -405,7 +405,7 @@ fn test_cache_moved() {
let mut bl = Backlog::new(config());
let prerender1 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: None,
};
@ -428,13 +428,13 @@ fn test_cache_moved() {
// New item added at bottom
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi")),
content: BufferItemContent::SimpleText(Text::raw("hi")),
prerender: &prerender1,
unique_id: None,
};
let prerender2 = Prerender::new();
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("world")),
content: BufferItemContent::SimpleText(Text::raw("world")),
prerender: &prerender2,
unique_id: None,
};
@ -462,17 +462,17 @@ fn test_overflow_and_scroll() {
let prerender3 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("line1 x")),
content: BufferItemContent::SimpleText(Text::raw("line1 x")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("line2 y\nline3 y\nline4 y")),
content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")),
prerender: &prerender2,
unique_id: None,
};
let item3 = BufferItem {
content: BufferItemContent::Text(Text::raw("line5 z")),
content: BufferItemContent::SimpleText(Text::raw("line5 z")),
prerender: &prerender3,
unique_id: None,
};
@ -498,17 +498,17 @@ fn test_overflow_and_scroll() {
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("line1 x")),
content: BufferItemContent::SimpleText(Text::raw("line1 x")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("line2 y\nline3 y\nline4 y")),
content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")),
prerender: &prerender2,
unique_id: None,
};
let item3 = BufferItem {
content: BufferItemContent::Text(Text::raw("line5 z")),
content: BufferItemContent::SimpleText(Text::raw("line5 z")),
prerender: &prerender3,
unique_id: None,
};
@ -534,17 +534,17 @@ fn test_overflow_and_scroll() {
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("line1 x")),
content: BufferItemContent::SimpleText(Text::raw("line1 x")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("line2 y\nline3 y\nline4 y")),
content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")),
prerender: &prerender2,
unique_id: None,
};
let item3 = BufferItem {
content: BufferItemContent::Text(Text::raw("line5 z")),
content: BufferItemContent::SimpleText(Text::raw("line5 z")),
prerender: &prerender3,
unique_id: None,
};
@ -569,17 +569,17 @@ fn test_overflow_and_scroll() {
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("line1 x")),
content: BufferItemContent::SimpleText(Text::raw("line1 x")),
prerender: &prerender1,
unique_id: None,
};
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("line2 y\nline3 y\nline4 y")),
content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")),
prerender: &prerender2,
unique_id: None,
};
let item3 = BufferItem {
content: BufferItemContent::Text(Text::raw("line5 z")),
content: BufferItemContent::SimpleText(Text::raw("line5 z")),
prerender: &prerender3,
unique_id: None,
};
@ -608,7 +608,7 @@ fn test_scrolledup_new_line() {
let mut bl = Backlog::new(config());
let prerender1 = Prerender::new();
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi\nworld")),
content: BufferItemContent::SimpleText(Text::raw("hi\nworld")),
prerender: &prerender1,
unique_id: Some(123),
};
@ -632,7 +632,7 @@ fn test_scrolledup_new_line() {
// Scroll up one line
bl.scroll_up(1);
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi\nworld")),
content: BufferItemContent::SimpleText(Text::raw("hi\nworld")),
prerender: &prerender1,
unique_id: Some(123),
};
@ -653,13 +653,13 @@ fn test_scrolledup_new_line() {
// New item added at bottom, displayed paragraph should not move up
let item1 = BufferItem {
content: BufferItemContent::Text(Text::raw("hi\nworld")),
content: BufferItemContent::SimpleText(Text::raw("hi\nworld")),
prerender: &prerender1,
unique_id: Some(123),
};
let prerender2 = Prerender::new();
let item2 = BufferItem {
content: BufferItemContent::Text(Text::raw("!")),
content: BufferItemContent::SimpleText(Text::raw("!")),
prerender: &prerender2,
unique_id: Some(456),
};