mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Layout API docs (#4103)
This commit is contained in:
parent
36040d93ef
commit
d859218b90
@ -17,7 +17,7 @@ pub struct Engine<'a> {
|
|||||||
/// Provides access to information about the document.
|
/// Provides access to information about the document.
|
||||||
pub introspector: Tracked<'a, Introspector>,
|
pub introspector: Tracked<'a, Introspector>,
|
||||||
/// The route the engine took during compilation. This is used to detect
|
/// The route the engine took during compilation. This is used to detect
|
||||||
/// cyclic imports and too much nesting.
|
/// cyclic imports and excessive nesting.
|
||||||
pub route: Route<'a>,
|
pub route: Route<'a>,
|
||||||
/// Provides stable identities to elements.
|
/// Provides stable identities to elements.
|
||||||
pub locator: &'a mut Locator<'a>,
|
pub locator: &'a mut Locator<'a>,
|
||||||
@ -45,8 +45,11 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The route the engine took during compilation. This is used to detect
|
/// The route the engine took during compilation. This is used to detect
|
||||||
/// cyclic imports and too much nesting.
|
/// cyclic imports and excessive nesting.
|
||||||
pub struct Route<'a> {
|
pub struct Route<'a> {
|
||||||
|
/// The parent route segment, if present.
|
||||||
|
///
|
||||||
|
/// This is used when an engine is created from another engine.
|
||||||
// We need to override the constraint's lifetime here so that `Tracked` is
|
// We need to override the constraint's lifetime here so that `Tracked` is
|
||||||
// covariant over the constraint. If it becomes invariant, we're in for a
|
// covariant over the constraint. If it becomes invariant, we're in for a
|
||||||
// world of lifetime pain.
|
// world of lifetime pain.
|
||||||
@ -60,9 +63,10 @@ pub struct Route<'a> {
|
|||||||
/// route exceeds `MAX_DEPTH`, then we throw a "maximum ... depth exceeded"
|
/// route exceeds `MAX_DEPTH`, then we throw a "maximum ... depth exceeded"
|
||||||
/// error.
|
/// error.
|
||||||
len: usize,
|
len: usize,
|
||||||
/// The upper bound we've established for the parent chain length. We don't
|
/// The upper bound we've established for the parent chain length.
|
||||||
/// know the exact length (that would defeat the whole purpose because it
|
///
|
||||||
/// would prevent cache reuse of some computation at different,
|
/// We don't know the exact length (that would defeat the whole purpose
|
||||||
|
/// because it would prevent cache reuse of some computation at different,
|
||||||
/// non-exceeding depths).
|
/// non-exceeding depths).
|
||||||
upper: AtomicUsize,
|
upper: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
@ -354,9 +354,13 @@ impl Hash for dyn Blockable {
|
|||||||
/// A show rule recipe.
|
/// A show rule recipe.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
/// The span errors are reported with.
|
/// The span that errors are reported with.
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// Determines whether the recipe applies to an element.
|
/// Determines whether the recipe applies to an element.
|
||||||
|
///
|
||||||
|
/// If this is `None`, then this recipe is from a show rule with
|
||||||
|
/// no selector (`show: rest => ...`), which is [eagerly applied][Content::styled_with_recipe]
|
||||||
|
/// to the rest of the content in the scope.
|
||||||
pub selector: Option<Selector>,
|
pub selector: Option<Selector>,
|
||||||
/// The transformation to perform on the match.
|
/// The transformation to perform on the match.
|
||||||
pub transform: Transformation,
|
pub transform: Transformation,
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
//! Layout flows.
|
||||||
|
//!
|
||||||
|
//! A *flow* is a collection of block-level layoutable elements.
|
||||||
|
//! This is analogous to a paragraph, which is a collection of
|
||||||
|
//! inline-level layoutable elements.
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
@ -20,7 +26,7 @@ use crate::util::Numeric;
|
|||||||
/// and the contents of boxes.
|
/// and the contents of boxes.
|
||||||
#[elem(Debug, LayoutMultiple)]
|
#[elem(Debug, LayoutMultiple)]
|
||||||
pub struct FlowElem {
|
pub struct FlowElem {
|
||||||
/// The children that will be arranges into a flow.
|
/// The children that will be arranged into a flow.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: Vec<Content>,
|
||||||
}
|
}
|
||||||
@ -96,10 +102,12 @@ struct FlowLayouter<'a> {
|
|||||||
/// subtracting.
|
/// subtracting.
|
||||||
initial: Size,
|
initial: Size,
|
||||||
/// Whether the last block was a paragraph.
|
/// Whether the last block was a paragraph.
|
||||||
|
///
|
||||||
|
/// Used for indenting paragraphs after the first in a block.
|
||||||
last_was_par: bool,
|
last_was_par: bool,
|
||||||
/// Spacing and layouted blocks for the current region.
|
/// Spacing and layouted blocks for the current region.
|
||||||
items: Vec<FlowItem>,
|
items: Vec<FlowItem>,
|
||||||
/// A queue of floats.
|
/// A queue of floating elements.
|
||||||
pending_floats: Vec<FlowItem>,
|
pending_floats: Vec<FlowItem>,
|
||||||
/// Whether we have any footnotes in the current region.
|
/// Whether we have any footnotes in the current region.
|
||||||
has_footnotes: bool,
|
has_footnotes: bool,
|
||||||
@ -123,10 +131,19 @@ enum FlowItem {
|
|||||||
Absolute(Abs, bool),
|
Absolute(Abs, bool),
|
||||||
/// Fractional spacing between other items.
|
/// Fractional spacing between other items.
|
||||||
Fractional(Fr),
|
Fractional(Fr),
|
||||||
/// A frame for a layouted block, how to align it, whether it sticks to the
|
/// A frame for a layouted block.
|
||||||
/// item after it (for orphan prevention), and whether it is movable
|
Frame {
|
||||||
/// (to keep it together with its footnotes).
|
/// The frame itself.
|
||||||
Frame { frame: Frame, align: Axes<FixedAlignment>, sticky: bool, movable: bool },
|
frame: Frame,
|
||||||
|
/// How to align the frame.
|
||||||
|
align: Axes<FixedAlignment>,
|
||||||
|
/// Whether the frame sticks to the item after it (for orphan prevention).
|
||||||
|
sticky: bool,
|
||||||
|
/// Whether the frame is movable; that is, kept together with its footnotes.
|
||||||
|
///
|
||||||
|
/// This is true for frames created by paragraphs and [`LayoutSingle`] elements.
|
||||||
|
movable: bool,
|
||||||
|
},
|
||||||
/// An absolutely placed frame.
|
/// An absolutely placed frame.
|
||||||
Placed {
|
Placed {
|
||||||
frame: Frame,
|
frame: Frame,
|
||||||
@ -143,7 +160,7 @@ enum FlowItem {
|
|||||||
impl FlowItem {
|
impl FlowItem {
|
||||||
/// Whether this item is out-of-flow.
|
/// Whether this item is out-of-flow.
|
||||||
///
|
///
|
||||||
/// Out-of-flow items are guaranteed to have a [`Size::zero()`].
|
/// Out-of-flow items are guaranteed to have a [zero size][Size::zero()].
|
||||||
fn is_out_of_flow(&self) -> bool {
|
fn is_out_of_flow(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Placed { float: false, .. } => true,
|
Self::Placed { float: false, .. } => true,
|
||||||
@ -235,6 +252,8 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
)?
|
)?
|
||||||
.into_frames();
|
.into_frames();
|
||||||
|
|
||||||
|
// If the first line doesn’t fit in this region, then defer any
|
||||||
|
// previous sticky frame to the next region (if available)
|
||||||
if let Some(first) = lines.first() {
|
if let Some(first) = lines.first() {
|
||||||
while !self.regions.size.y.fits(first.height()) && !self.regions.in_last() {
|
while !self.regions.size.y.fits(first.height()) && !self.regions.in_last() {
|
||||||
let mut sticky = self.items.len();
|
let mut sticky = self.items.len();
|
||||||
@ -641,6 +660,9 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FlowLayouter<'_> {
|
impl FlowLayouter<'_> {
|
||||||
|
/// Tries to process all footnotes in the frame, placing them
|
||||||
|
/// in the next region if they could not be placed in the current
|
||||||
|
/// one.
|
||||||
fn try_handle_footnotes(
|
fn try_handle_footnotes(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
@ -663,6 +685,9 @@ impl FlowLayouter<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes all footnotes in the frame.
|
/// Processes all footnotes in the frame.
|
||||||
|
///
|
||||||
|
/// Returns true if the footnote entries fit in the allotted
|
||||||
|
/// regions.
|
||||||
fn handle_footnotes(
|
fn handle_footnotes(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
@ -285,6 +285,10 @@ impl Frame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Attach the metadata from this style chain to the frame.
|
/// Attach the metadata from this style chain to the frame.
|
||||||
|
///
|
||||||
|
/// If `force` is true, then the metadata is attached even when
|
||||||
|
/// the frame is empty.
|
||||||
|
// TODO: when would you want to pass true to `force` as opposed to false?
|
||||||
pub fn meta(&mut self, styles: StyleChain, force: bool) {
|
pub fn meta(&mut self, styles: StyleChain, force: bool) {
|
||||||
if force || !self.is_empty() {
|
if force || !self.is_empty() {
|
||||||
self.meta_iter(MetaElem::data_in(styles));
|
self.meta_iter(MetaElem::data_in(styles));
|
||||||
@ -456,6 +460,8 @@ pub enum FrameKind {
|
|||||||
#[default]
|
#[default]
|
||||||
Soft,
|
Soft,
|
||||||
/// A container which uses its own size.
|
/// A container which uses its own size.
|
||||||
|
///
|
||||||
|
/// This is used for page, block, box, column, grid, and stack elements.
|
||||||
Hard,
|
Hard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,11 @@ pub fn define(global: &mut Scope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Root-level layout.
|
/// Root-level layout.
|
||||||
|
///
|
||||||
|
/// This produces a complete document and is implemented for
|
||||||
|
/// [`DocumentElem`][crate::model::DocumentElem]. Any [`Content`]
|
||||||
|
/// can also be laid out at root level, in which case it is
|
||||||
|
/// wrapped inside a document element.
|
||||||
pub trait LayoutRoot {
|
pub trait LayoutRoot {
|
||||||
/// Layout into a document with one frame per page.
|
/// Layout into a document with one frame per page.
|
||||||
fn layout_root(
|
fn layout_root(
|
||||||
@ -128,7 +133,10 @@ pub trait LayoutRoot {
|
|||||||
) -> SourceResult<Document>;
|
) -> SourceResult<Document>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout into multiple regions.
|
/// Layout into multiple [regions][Regions].
|
||||||
|
///
|
||||||
|
/// This is more appropriate for elements that, for example, can be
|
||||||
|
/// laid out across multiple pages or columns.
|
||||||
pub trait LayoutMultiple {
|
pub trait LayoutMultiple {
|
||||||
/// Layout into one frame per region.
|
/// Layout into one frame per region.
|
||||||
fn layout(
|
fn layout(
|
||||||
@ -160,7 +168,10 @@ pub trait LayoutMultiple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout into a single region.
|
/// Layout into a single [region][Regions].
|
||||||
|
///
|
||||||
|
/// This is more appropriate for elements that don't make sense to
|
||||||
|
/// layout across multiple pages or columns, such as shapes.
|
||||||
pub trait LayoutSingle {
|
pub trait LayoutSingle {
|
||||||
/// Layout into one frame per region.
|
/// Layout into one frame per region.
|
||||||
fn layout(
|
fn layout(
|
||||||
|
@ -3,6 +3,12 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use crate::layout::{Abs, Axes, Size};
|
use crate::layout::{Abs, Axes, Size};
|
||||||
|
|
||||||
/// A sequence of regions to layout into.
|
/// A sequence of regions to layout into.
|
||||||
|
///
|
||||||
|
/// A *region* is a contiguous rectangular space in which elements
|
||||||
|
/// can be laid out. All regions within a `Regions` object have the
|
||||||
|
/// same width, namely `self.size.x`. This means that it is not
|
||||||
|
/// currently possible to, for instance, have content wrap to the
|
||||||
|
/// side of a floating element.
|
||||||
#[derive(Copy, Clone, Hash)]
|
#[derive(Copy, Clone, Hash)]
|
||||||
pub struct Regions<'a> {
|
pub struct Regions<'a> {
|
||||||
/// The remaining size of the first region.
|
/// The remaining size of the first region.
|
||||||
|
@ -8,7 +8,7 @@ use crate::layout::{
|
|||||||
|
|
||||||
/// Moves content without affecting layout.
|
/// Moves content without affecting layout.
|
||||||
///
|
///
|
||||||
/// The `move` function allows you to move content while th layout still 'sees'
|
/// The `move` function allows you to move content while the layout still 'sees'
|
||||||
/// it at the original positions. Containers will still be sized as if the
|
/// it at the original positions. Containers will still be sized as if the
|
||||||
/// content was not moved.
|
/// content was not moved.
|
||||||
///
|
///
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
//! Realization of content.
|
//! Realization of content.
|
||||||
|
//!
|
||||||
|
//! *Realization* is the process of applying show rules to produce
|
||||||
|
//! something that can be laid out directly.
|
||||||
|
//!
|
||||||
|
//! Currently, there are issues with the realization process, and
|
||||||
|
//! it is subject to changes in the future.
|
||||||
|
|
||||||
mod arenas;
|
mod arenas;
|
||||||
mod behaviour;
|
mod behaviour;
|
||||||
@ -98,11 +104,13 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a piece of content to this builder.
|
||||||
fn accept(
|
fn accept(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut content: &'a Content,
|
mut content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
|
// Implicitly wrap math content in an equation if needed
|
||||||
if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() {
|
if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() {
|
||||||
content = self
|
content = self
|
||||||
.arenas
|
.arenas
|
||||||
@ -133,6 +141,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to merge `content` with an element under construction
|
||||||
|
|
||||||
if self.cites.accept(content, styles) {
|
if self.cites.accept(content, styles) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -227,6 +237,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interrupts citation grouping and adds the resulting citation group to the builder.
|
||||||
fn interrupt_cites(&mut self) -> SourceResult<()> {
|
fn interrupt_cites(&mut self) -> SourceResult<()> {
|
||||||
if !self.cites.items.is_empty() {
|
if !self.cites.items.is_empty() {
|
||||||
let staged = mem::take(&mut self.cites.staged);
|
let staged = mem::take(&mut self.cites.staged);
|
||||||
@ -239,6 +250,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interrupts list building and adds the resulting list element to the builder.
|
||||||
fn interrupt_list(&mut self) -> SourceResult<()> {
|
fn interrupt_list(&mut self) -> SourceResult<()> {
|
||||||
self.interrupt_cites()?;
|
self.interrupt_cites()?;
|
||||||
if !self.list.items.is_empty() {
|
if !self.list.items.is_empty() {
|
||||||
@ -252,6 +264,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interrupts paragraph building and adds the resulting paragraph element to the builder.
|
||||||
fn interrupt_par(&mut self) -> SourceResult<()> {
|
fn interrupt_par(&mut self) -> SourceResult<()> {
|
||||||
self.interrupt_list()?;
|
self.interrupt_list()?;
|
||||||
if !self.par.0.is_empty() {
|
if !self.par.0.is_empty() {
|
||||||
@ -262,6 +275,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interrupts page building and adds the resulting page element to the builder.
|
||||||
fn interrupt_page(
|
fn interrupt_page(
|
||||||
&mut self,
|
&mut self,
|
||||||
styles: Option<StyleChain<'a>>,
|
styles: Option<StyleChain<'a>>,
|
||||||
@ -284,7 +298,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts pagebreaks and pages.
|
/// Builds a [document][DocumentElem] from pagebreaks and pages.
|
||||||
struct DocBuilder<'a> {
|
struct DocBuilder<'a> {
|
||||||
/// The page runs built so far.
|
/// The page runs built so far.
|
||||||
pages: BehavedBuilder<'a>,
|
pages: BehavedBuilder<'a>,
|
||||||
@ -295,6 +309,12 @@ struct DocBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DocBuilder<'a> {
|
impl<'a> DocBuilder<'a> {
|
||||||
|
/// Tries to accept a piece of content.
|
||||||
|
///
|
||||||
|
/// Returns true if this content could be merged into the document.
|
||||||
|
/// If this function returns false, then the
|
||||||
|
/// content could not be merged, and document building should be
|
||||||
|
/// interrupted so that the content can be added elsewhere.
|
||||||
fn accept(
|
fn accept(
|
||||||
&mut self,
|
&mut self,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
@ -324,6 +344,8 @@ impl<'a> DocBuilder<'a> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns this builder into the resulting document, along with
|
||||||
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<DocumentElem>, StyleChain<'a>) {
|
fn finish(self) -> (Packed<DocumentElem>, StyleChain<'a>) {
|
||||||
let (children, trunk, span) = self.pages.finish();
|
let (children, trunk, span) = self.pages.finish();
|
||||||
(Packed::new(DocumentElem::new(children)).spanned(span), trunk)
|
(Packed::new(DocumentElem::new(children)).spanned(span), trunk)
|
||||||
@ -340,11 +362,17 @@ impl Default for DocBuilder<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts flow content.
|
/// Builds a [flow][FlowElem] from flow content.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
|
struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
|
||||||
|
|
||||||
impl<'a> FlowBuilder<'a> {
|
impl<'a> FlowBuilder<'a> {
|
||||||
|
/// Tries to accept a piece of content.
|
||||||
|
///
|
||||||
|
/// Returns true if this content could be merged into the flow.
|
||||||
|
/// If this function returns false, then the
|
||||||
|
/// content could not be merged, and flow building should be
|
||||||
|
/// interrupted so that the content can be added elsewhere.
|
||||||
fn accept(
|
fn accept(
|
||||||
&mut self,
|
&mut self,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
@ -403,17 +431,25 @@ impl<'a> FlowBuilder<'a> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns this builder into the resulting flow, along with
|
||||||
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<FlowElem>, StyleChain<'a>) {
|
fn finish(self) -> (Packed<FlowElem>, StyleChain<'a>) {
|
||||||
let (children, trunk, span) = self.0.finish();
|
let (children, trunk, span) = self.0.finish();
|
||||||
(Packed::new(FlowElem::new(children)).spanned(span), trunk)
|
(Packed::new(FlowElem::new(children)).spanned(span), trunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts paragraph content.
|
/// Builds a [paragraph][ParElem] from paragraph content.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ParBuilder<'a>(BehavedBuilder<'a>);
|
struct ParBuilder<'a>(BehavedBuilder<'a>);
|
||||||
|
|
||||||
impl<'a> ParBuilder<'a> {
|
impl<'a> ParBuilder<'a> {
|
||||||
|
/// Tries to accept a piece of content.
|
||||||
|
///
|
||||||
|
/// Returns true if this content could be merged into the paragraph.
|
||||||
|
/// If this function returns false, then the
|
||||||
|
/// content could not be merged, and paragraph building should be
|
||||||
|
/// interrupted so that the content can be added elsewhere.
|
||||||
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
||||||
if content.is::<MetaElem>() {
|
if content.is::<MetaElem>() {
|
||||||
if !self.0.is_empty() {
|
if !self.0.is_empty() {
|
||||||
@ -437,13 +473,16 @@ impl<'a> ParBuilder<'a> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns this builder into the resulting paragraph, along with
|
||||||
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<ParElem>, StyleChain<'a>) {
|
fn finish(self) -> (Packed<ParElem>, StyleChain<'a>) {
|
||||||
let (children, trunk, span) = self.0.finish();
|
let (children, trunk, span) = self.0.finish();
|
||||||
(Packed::new(ParElem::new(children)).spanned(span), trunk)
|
(Packed::new(ParElem::new(children)).spanned(span), trunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts list / enum items, spaces, paragraph breaks.
|
/// Builds a list (either [`ListElem`], [`EnumElem`], or [`TermsElem`])
|
||||||
|
/// from list or enum items, spaces, and paragraph breaks.
|
||||||
struct ListBuilder<'a> {
|
struct ListBuilder<'a> {
|
||||||
/// The list items collected so far.
|
/// The list items collected so far.
|
||||||
items: BehavedBuilder<'a>,
|
items: BehavedBuilder<'a>,
|
||||||
@ -454,6 +493,12 @@ struct ListBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ListBuilder<'a> {
|
impl<'a> ListBuilder<'a> {
|
||||||
|
/// Tries to accept a piece of content.
|
||||||
|
///
|
||||||
|
/// Returns true if this content could be merged into the list.
|
||||||
|
/// If this function returns false, then the
|
||||||
|
/// content could not be merged, and list building should be
|
||||||
|
/// interrupted so that the content can be added elsewhere.
|
||||||
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
||||||
if !self.items.is_empty()
|
if !self.items.is_empty()
|
||||||
&& (content.is::<SpaceElem>() || content.is::<ParbreakElem>())
|
&& (content.is::<SpaceElem>() || content.is::<ParbreakElem>())
|
||||||
@ -479,6 +524,8 @@ impl<'a> ListBuilder<'a> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns this builder into the resulting list, along with
|
||||||
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Content, StyleChain<'a>) {
|
fn finish(self) -> (Content, StyleChain<'a>) {
|
||||||
let (items, trunk, span) = self.items.finish_iter();
|
let (items, trunk, span) = self.items.finish_iter();
|
||||||
let mut items = items.peekable();
|
let mut items = items.peekable();
|
||||||
@ -545,7 +592,7 @@ impl Default for ListBuilder<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts citations.
|
/// Builds a [citation group][CiteGroup] from citations.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CiteGroupBuilder<'a> {
|
struct CiteGroupBuilder<'a> {
|
||||||
/// The styles.
|
/// The styles.
|
||||||
@ -557,6 +604,12 @@ struct CiteGroupBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CiteGroupBuilder<'a> {
|
impl<'a> CiteGroupBuilder<'a> {
|
||||||
|
/// Tries to accept a piece of content.
|
||||||
|
///
|
||||||
|
/// Returns true if this content could be merged into the citation
|
||||||
|
/// group. If this function returns false, then the
|
||||||
|
/// content could not be merged, and citation grouping should be
|
||||||
|
/// interrupted so that the content can be added elsewhere.
|
||||||
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
||||||
if !self.items.is_empty()
|
if !self.items.is_empty()
|
||||||
&& (content.is::<SpaceElem>() || content.is::<MetaElem>())
|
&& (content.is::<SpaceElem>() || content.is::<MetaElem>())
|
||||||
@ -577,6 +630,8 @@ impl<'a> CiteGroupBuilder<'a> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns this builder into the resulting citation group, along with
|
||||||
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<CiteGroup>, StyleChain<'a>) {
|
fn finish(self) -> (Packed<CiteGroup>, StyleChain<'a>) {
|
||||||
let span = self.items.first().map(|cite| cite.span()).unwrap_or(Span::detached());
|
let span = self.items.first().map(|cite| cite.span()).unwrap_or(Span::detached());
|
||||||
(Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles)
|
(Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles)
|
||||||
|
@ -15,7 +15,7 @@ use crate::util::{hash128, BitSet};
|
|||||||
|
|
||||||
/// What to do with an element when encountering it during realization.
|
/// What to do with an element when encountering it during realization.
|
||||||
struct Verdict<'a> {
|
struct Verdict<'a> {
|
||||||
/// Whether the element is already prepated (i.e. things that should only
|
/// Whether the element is already prepared (i.e. things that should only
|
||||||
/// happen once have happened).
|
/// happen once have happened).
|
||||||
prepared: bool,
|
prepared: bool,
|
||||||
/// A map of styles to apply to the element.
|
/// A map of styles to apply to the element.
|
||||||
@ -32,7 +32,7 @@ enum ShowStep<'a> {
|
|||||||
Builtin,
|
Builtin,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the `target` element needs processing.
|
/// Returns whether the `target` element needs processing.
|
||||||
pub fn processable<'a>(
|
pub fn processable<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
target: &'a Content,
|
target: &'a Content,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user