From 49c0bac44dda8be643480df2c4e68623eeec91bd Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 24 Feb 2022 18:47:43 +0100 Subject: [PATCH] First-line indents Co-Authored-By: Martin Haug --- src/eval/collapse.rs | 5 +++++ src/eval/styles.rs | 16 ++++++++++++++++ src/eval/template.rs | 30 +++++++++++++++++++++++++++++- src/library/par.rs | 3 +++ tests/ref/text/indent.png | Bin 0 -> 37966 bytes tests/typ/text/indent.typ | 28 ++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/ref/text/indent.png create mode 100644 tests/typ/text/indent.typ diff --git a/src/eval/collapse.rs b/src/eval/collapse.rs index ef8a5255e..31581986b 100644 --- a/src/eval/collapse.rs +++ b/src/eval/collapse.rs @@ -67,6 +67,11 @@ impl<'a, T: Merge> CollapsingBuilder<'a, T> { self.staged.push((item, styles, None)); } + /// Iterate over the contained items. + pub fn items(&self) -> impl DoubleEndedIterator { + self.builder.items().chain(self.staged.iter().map(|(item, ..)| item)) + } + /// Return the finish style vec and the common prefix chain. pub fn finish(mut self) -> (StyleVec, StyleChain<'a>) { self.flush(false); diff --git a/src/eval/styles.rs b/src/eval/styles.rs index bd1f808f9..8e7bd4b65 100644 --- a/src/eval/styles.rs +++ b/src/eval/styles.rs @@ -547,6 +547,17 @@ impl StyleVec { .flat_map(|(map, count)| std::iter::repeat(map).take(*count)); self.items().zip(styles) } + + /// Insert an element in the front. The element will share the style of the + /// current first element. + /// + /// This method has no effect if the vector is empty. + pub fn push_front(&mut self, item: T) { + if !self.maps.is_empty() { + self.items.insert(0, item); + self.maps[0].1 += 1; + } + } } impl Default for StyleVec { @@ -601,6 +612,11 @@ impl<'a, T> StyleVecBuilder<'a, T> { Some((item, chain)) } + /// Iterate over the contained items. + pub fn items(&self) -> std::slice::Iter<'_, T> { + self.items.iter() + } + /// Finish building, returning a pair of two things: /// - a style vector of items with the non-shared styles /// - a shared prefix chain of styles that apply to all items diff --git a/src/eval/template.rs b/src/eval/template.rs index a9e1262f5..fb6246002 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -445,8 +445,36 @@ impl<'a> Builder<'a> { /// Finish the currently built paragraph. fn finish_par(&mut self, styles: StyleChain<'a>) { - let (par, shared) = std::mem::take(&mut self.par).finish(); + let (mut par, shared) = std::mem::take(&mut self.par).finish(); if !par.is_empty() { + // Paragraph indent should only apply if the paragraph starts with + // text and follows directly after another paragraph. + let indent = shared.get(ParNode::INDENT); + if !indent.is_zero() + && par + .items() + .find_map(|child| match child { + ParChild::Spacing(_) => None, + ParChild::Text(_) => Some(true), + ParChild::Node(_) => Some(false), + }) + .unwrap_or_default() + && self + .flow + .items() + .rev() + .find_map(|child| match child { + FlowChild::Leading => None, + FlowChild::Parbreak => None, + FlowChild::Node(node) => Some(node.is::()), + FlowChild::Spacing(_) => Some(false), + FlowChild::Colbreak => Some(false), + }) + .unwrap_or_default() + { + par.push_front(ParChild::Spacing(SpacingKind::Linear(indent))) + } + let node = ParNode(par).pack(); self.flow.supportive(FlowChild::Node(node), shared); } diff --git a/src/library/par.rs b/src/library/par.rs index e51221664..cc5dd9b6d 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -36,6 +36,8 @@ impl ParNode { pub const LEADING: Linear = Relative::new(0.65).into(); /// The extra spacing between paragraphs (dependent on scaled font size). pub const SPACING: Linear = Relative::new(0.55).into(); + /// The indent the first line of a consecutive paragraph should have. + pub const INDENT: Linear = Linear::zero(); fn construct(_: &mut Context, args: &mut Args) -> TypResult