From 0500d51f8cc72d34b3585d35152fada94661f573 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sat, 7 Oct 2023 07:25:57 -0700 Subject: [PATCH] Add tests for table rendering --- src/builder.rs | 45 +++++++++++++++++++++++++++++++++++------- src/diff.rs | 12 +++++------ src/markdown/text.rs | 7 ++++++- src/presentation.rs | 2 +- src/render/draw.rs | 4 ++-- src/render/operator.rs | 2 +- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 7882a2e..354aa37 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -329,7 +329,7 @@ impl<'a> PresentationBuilder<'a> { } if !texts.is_empty() { self.slide_operations.push(RenderOperation::RenderTextLine { - texts: WeightedLine::from(texts), + line: WeightedLine::from(texts), alignment: alignment.clone(), }); } @@ -408,12 +408,15 @@ impl<'a> PresentationBuilder<'a> { let mut separator = Text { chunks: Vec::new() }; for (index, width) in widths.iter().enumerate() { let mut contents = String::new(); - let mut extra_lines = 1; + let mut margin = 1; if index > 0 { contents.push('┼'); - extra_lines += 1; + // Append an extra dash to have 1 column margin on both sides + if index < widths.len() - 1 { + margin += 1; + } } - contents.extend(iter::repeat("─").take(*width + extra_lines)); + contents.extend(iter::repeat("─").take(*width + margin)); separator.chunks.push(StyledText::from(contents)); } @@ -480,7 +483,7 @@ impl AsRenderOperations for FooterGenerator { operations.extend([ RenderOperation::JumpToWindowBottom, RenderOperation::RenderTextLine { - texts: vec![Self::render_template(left, ¤t_slide, &context, colors.clone())].into(), + line: vec![Self::render_template(left, ¤t_slide, &context, colors.clone())].into(), alignment: Alignment::Left { margin: 1 }, }, ]); @@ -489,7 +492,7 @@ impl AsRenderOperations for FooterGenerator { operations.extend([ RenderOperation::JumpToWindowBottom, RenderOperation::RenderTextLine { - texts: vec![Self::render_template(right, ¤t_slide, &context, colors.clone())].into(), + line: vec![Self::render_template(right, ¤t_slide, &context, colors.clone())].into(), alignment: Alignment::Right { margin: 1 }, }, ]); @@ -505,7 +508,7 @@ impl AsRenderOperations for FooterGenerator { let bar = vec![WeightedText::from(StyledText::new(bar, TextStyle::default().colors(colors.clone())))]; vec![ RenderOperation::JumpToWindowBottom, - RenderOperation::RenderTextLine { texts: bar.into(), alignment: Alignment::Left { margin: 0 } }, + RenderOperation::RenderTextLine { line: bar.into(), alignment: Alignment::Left { margin: 0 } }, ] } FooterStyle::Empty => vec![], @@ -564,6 +567,20 @@ mod test { } } + fn extract_text_lines(operations: &[RenderOperation]) -> Vec { + let mut output = Vec::new(); + for operation in operations { + match operation { + RenderOperation::RenderTextLine { line, .. } => { + let texts: Vec<_> = line.iter_texts().map(|text| text.text.text.clone()).collect(); + output.push(texts.join("")); + } + _ => (), + }; + } + output + } + #[test] fn prelude_appears_once() { let elements = vec![ @@ -629,4 +646,18 @@ mod test { assert_eq!(lengths[0], (width, width)); assert_eq!(lengths[1], (width, width)); } + + #[test] + fn table() { + let elements = vec![MarkdownElement::Table(Table { + header: TableRow(vec![Text::from("key"), Text::from("value"), Text::from("other")]), + rows: vec![TableRow(vec![Text::from("potato"), Text::from("bar"), Text::from("yes")])], + })]; + let slides = build_presentation(elements).into_slides(); + let operations: Vec<_> = + slides.into_iter().next().unwrap().render_operations.into_iter().filter(|op| is_visible(op)).collect(); + let lines = extract_text_lines(&operations); + let expected_lines = &["key │ value │ other", "───────┼───────┼──────", "potato │ bar │ yes "]; + assert_eq!(lines, expected_lines); + } } diff --git a/src/diff.rs b/src/diff.rs index 46c7f7c..b00b5e5 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -50,7 +50,7 @@ impl ContentDiff for RenderOperation { match (self, other) { (SetColors(original), SetColors(updated)) if original != updated => false, - (RenderTextLine { texts: original, .. }, RenderTextLine { texts: updated, .. }) if original != updated => { + (RenderTextLine { line: original, .. }, RenderTextLine { line: updated, .. }) if original != updated => { true } (RenderTextLine { alignment: original, .. }, RenderTextLine { alignment: updated, .. }) @@ -114,7 +114,7 @@ mod test { #[case(RenderOperation::RenderSeparator)] #[case(RenderOperation::RenderLineBreak)] #[case(RenderOperation::SetColors(Colors{background: None, foreground: None}))] - #[case(RenderOperation::RenderTextLine{texts: String::from("asd").into(), alignment: Default::default()})] + #[case(RenderOperation::RenderTextLine{line: String::from("asd").into(), alignment: Default::default()})] #[case(RenderOperation::RenderPreformattedLine( PreformattedLine{ text: "asd".into(), @@ -131,19 +131,19 @@ mod test { #[test] fn different_text() { - let lhs = RenderOperation::RenderTextLine { texts: String::from("foo").into(), alignment: Default::default() }; - let rhs = RenderOperation::RenderTextLine { texts: String::from("bar").into(), alignment: Default::default() }; + let lhs = RenderOperation::RenderTextLine { line: String::from("foo").into(), alignment: Default::default() }; + let rhs = RenderOperation::RenderTextLine { line: String::from("bar").into(), alignment: Default::default() }; assert!(lhs.is_content_different(&rhs)); } #[test] fn different_text_alignment() { let lhs = RenderOperation::RenderTextLine { - texts: String::from("foo").into(), + line: String::from("foo").into(), alignment: Alignment::Left { margin: 42 }, }; let rhs = RenderOperation::RenderTextLine { - texts: String::from("foo").into(), + line: String::from("foo").into(), alignment: Alignment::Left { margin: 1337 }, }; assert!(!lhs.is_content_different(&rhs)); diff --git a/src/markdown/text.rs b/src/markdown/text.rs index 707e99f..a1dcbfa 100644 --- a/src/markdown/text.rs +++ b/src/markdown/text.rs @@ -18,6 +18,11 @@ impl WeightedLine { pub fn width(&self) -> usize { self.0.iter().map(|text| text.width()).sum() } + + /// Get an iterator to the underlying text chunks. + pub fn iter_texts(&self) -> impl Iterator { + self.0.iter() + } } impl From> for WeightedLine { @@ -42,7 +47,7 @@ struct CharAccumulator { /// A piece of weighted text. #[derive(Clone, Debug, PartialEq, Eq)] pub struct WeightedText { - text: StyledText, + pub text: StyledText, accumulators: Vec, } diff --git a/src/presentation.rs b/src/presentation.rs index 4be0ddf..d8e6d50 100644 --- a/src/presentation.rs +++ b/src/presentation.rs @@ -168,7 +168,7 @@ pub enum RenderOperation { JumpToSlideBottom, /// Render a line of text. - RenderTextLine { texts: WeightedLine, alignment: Alignment }, + RenderTextLine { line: WeightedLine, alignment: Alignment }, /// Render a horizontal separator line. RenderSeparator, diff --git a/src/render/draw.rs b/src/render/draw.rs index caea377..afa4092 100644 --- a/src/render/draw.rs +++ b/src/render/draw.rs @@ -64,10 +64,10 @@ where RenderOperation::ClearScreen, RenderOperation::SetColors(Colors { foreground: Some(Color::Red), background: Some(Color::Black) }), RenderOperation::JumpToVerticalCenter, - RenderOperation::RenderTextLine { texts: WeightedLine::from(heading), alignment: alignment.clone() }, + RenderOperation::RenderTextLine { line: WeightedLine::from(heading), alignment: alignment.clone() }, RenderOperation::RenderLineBreak, RenderOperation::RenderLineBreak, - RenderOperation::RenderTextLine { texts: WeightedLine::from(error), alignment: alignment.clone() }, + RenderOperation::RenderTextLine { line: WeightedLine::from(error), alignment: alignment.clone() }, ]; let mut operator = RenderOperator::new(&mut self.handle, dimensions.clone(), dimensions, Default::default()); for operation in operations { diff --git a/src/render/operator.rs b/src/render/operator.rs index 3e4c2ba..c496140 100644 --- a/src/render/operator.rs +++ b/src/render/operator.rs @@ -44,7 +44,7 @@ where RenderOperation::JumpToVerticalCenter => self.jump_to_vertical_center(), RenderOperation::JumpToSlideBottom => self.jump_to_slide_bottom(), RenderOperation::JumpToWindowBottom => self.jump_to_window_bottom(), - RenderOperation::RenderTextLine { texts, alignment } => self.render_text(texts, alignment), + RenderOperation::RenderTextLine { line: texts, alignment } => self.render_text(texts, alignment), RenderOperation::RenderSeparator => self.render_separator(), RenderOperation::RenderLineBreak => self.render_line_break(), RenderOperation::RenderImage(image) => self.render_image(image),