diff --git a/crates/typst/src/engine.rs b/crates/typst/src/engine.rs index f42ea1927..03e4957ce 100644 --- a/crates/typst/src/engine.rs +++ b/crates/typst/src/engine.rs @@ -17,7 +17,7 @@ pub struct Engine<'a> { /// Provides access to information about the document. pub introspector: Tracked<'a, Introspector>, /// 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>, /// Provides stable identities to elements. pub locator: &'a mut Locator<'a>, @@ -45,8 +45,11 @@ impl Engine<'_> { } /// 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> { + /// 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 // covariant over the constraint. If it becomes invariant, we're in for a // world of lifetime pain. @@ -60,9 +63,10 @@ pub struct Route<'a> { /// route exceeds `MAX_DEPTH`, then we throw a "maximum ... depth exceeded" /// error. len: usize, - /// The upper bound we've established for the parent chain length. We don't - /// know the exact length (that would defeat the whole purpose because it - /// would prevent cache reuse of some computation at different, + /// The upper bound we've established for the parent chain length. + /// + /// 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). upper: AtomicUsize, } diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index 35460088c..9b434a1ec 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -354,9 +354,13 @@ impl Hash for dyn Blockable { /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] pub struct Recipe { - /// The span errors are reported with. + /// The span that errors are reported with. pub span: Span, /// 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, /// The transformation to perform on the match. pub transform: Transformation, diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs index 3cc53c6a4..1e09ed830 100644 --- a/crates/typst/src/layout/flow.rs +++ b/crates/typst/src/layout/flow.rs @@ -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 crate::diag::{bail, SourceResult}; @@ -20,7 +26,7 @@ use crate::util::Numeric; /// and the contents of boxes. #[elem(Debug, LayoutMultiple)] pub struct FlowElem { - /// The children that will be arranges into a flow. + /// The children that will be arranged into a flow. #[variadic] pub children: Vec, } @@ -96,10 +102,12 @@ struct FlowLayouter<'a> { /// subtracting. initial: Size, /// Whether the last block was a paragraph. + /// + /// Used for indenting paragraphs after the first in a block. last_was_par: bool, /// Spacing and layouted blocks for the current region. items: Vec, - /// A queue of floats. + /// A queue of floating elements. pending_floats: Vec, /// Whether we have any footnotes in the current region. has_footnotes: bool, @@ -123,10 +131,19 @@ enum FlowItem { Absolute(Abs, bool), /// Fractional spacing between other items. Fractional(Fr), - /// A frame for a layouted block, how to align it, whether it sticks to the - /// item after it (for orphan prevention), and whether it is movable - /// (to keep it together with its footnotes). - Frame { frame: Frame, align: Axes, sticky: bool, movable: bool }, + /// A frame for a layouted block. + Frame { + /// The frame itself. + frame: Frame, + /// How to align the frame. + align: Axes, + /// 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. Placed { frame: Frame, @@ -143,7 +160,7 @@ enum FlowItem { impl FlowItem { /// 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 { match self { Self::Placed { float: false, .. } => true, @@ -235,6 +252,8 @@ impl<'a> FlowLayouter<'a> { )? .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() { while !self.regions.size.y.fits(first.height()) && !self.regions.in_last() { let mut sticky = self.items.len(); @@ -641,6 +660,9 @@ impl<'a> FlowLayouter<'a> { } 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( &mut self, engine: &mut Engine, @@ -663,6 +685,9 @@ impl FlowLayouter<'_> { } /// Processes all footnotes in the frame. + /// + /// Returns true if the footnote entries fit in the allotted + /// regions. fn handle_footnotes( &mut self, engine: &mut Engine, diff --git a/crates/typst/src/layout/frame.rs b/crates/typst/src/layout/frame.rs index b42235267..61e2d65f6 100644 --- a/crates/typst/src/layout/frame.rs +++ b/crates/typst/src/layout/frame.rs @@ -285,6 +285,10 @@ impl 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) { if force || !self.is_empty() { self.meta_iter(MetaElem::data_in(styles)); @@ -456,6 +460,8 @@ pub enum FrameKind { #[default] Soft, /// A container which uses its own size. + /// + /// This is used for page, block, box, column, grid, and stack elements. Hard, } diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs index 7f0c3916c..23a08c5f1 100644 --- a/crates/typst/src/layout/mod.rs +++ b/crates/typst/src/layout/mod.rs @@ -119,6 +119,11 @@ pub fn define(global: &mut Scope) { } /// 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 { /// Layout into a document with one frame per page. fn layout_root( @@ -128,7 +133,10 @@ pub trait LayoutRoot { ) -> SourceResult; } -/// 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 { /// Layout into one frame per region. 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 { /// Layout into one frame per region. fn layout( diff --git a/crates/typst/src/layout/regions.rs b/crates/typst/src/layout/regions.rs index 46e85e68c..d8807f377 100644 --- a/crates/typst/src/layout/regions.rs +++ b/crates/typst/src/layout/regions.rs @@ -3,6 +3,12 @@ use std::fmt::{self, Debug, Formatter}; use crate::layout::{Abs, Axes, Size}; /// 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)] pub struct Regions<'a> { /// The remaining size of the first region. diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs index 13ff66ae9..85bbb3625 100644 --- a/crates/typst/src/layout/transform.rs +++ b/crates/typst/src/layout/transform.rs @@ -8,7 +8,7 @@ use crate::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 /// content was not moved. /// diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index 99b12a18d..e06059ff9 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -1,4 +1,10 @@ //! 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 behaviour; @@ -98,11 +104,13 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { } } + /// Adds a piece of content to this builder. fn accept( &mut self, mut content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<()> { + // Implicitly wrap math content in an equation if needed if content.can::() && !content.is::() { content = self .arenas @@ -133,6 +141,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { return Ok(()); } + // Try to merge `content` with an element under construction + if self.cites.accept(content, styles) { return Ok(()); } @@ -227,6 +237,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { Ok(()) } + /// Interrupts citation grouping and adds the resulting citation group to the builder. fn interrupt_cites(&mut self) -> SourceResult<()> { if !self.cites.items.is_empty() { let staged = mem::take(&mut self.cites.staged); @@ -239,6 +250,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { Ok(()) } + /// Interrupts list building and adds the resulting list element to the builder. fn interrupt_list(&mut self) -> SourceResult<()> { self.interrupt_cites()?; if !self.list.items.is_empty() { @@ -252,6 +264,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { Ok(()) } + /// Interrupts paragraph building and adds the resulting paragraph element to the builder. fn interrupt_par(&mut self) -> SourceResult<()> { self.interrupt_list()?; if !self.par.0.is_empty() { @@ -262,6 +275,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { Ok(()) } + /// Interrupts page building and adds the resulting page element to the builder. fn interrupt_page( &mut self, styles: Option>, @@ -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> { /// The page runs built so far. pages: BehavedBuilder<'a>, @@ -295,6 +309,12 @@ struct 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( &mut self, arenas: &'a Arenas<'a>, @@ -324,6 +344,8 @@ impl<'a> DocBuilder<'a> { false } + /// Turns this builder into the resulting document, along with + /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { let (children, trunk, span) = self.pages.finish(); (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)] struct FlowBuilder<'a>(BehavedBuilder<'a>, bool); 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( &mut self, arenas: &'a Arenas<'a>, @@ -403,17 +431,25 @@ impl<'a> FlowBuilder<'a> { false } + /// Turns this builder into the resulting flow, along with + /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { let (children, trunk, span) = self.0.finish(); (Packed::new(FlowElem::new(children)).spanned(span), trunk) } } -/// Accepts paragraph content. +/// Builds a [paragraph][ParElem] from paragraph content. #[derive(Default)] struct ParBuilder<'a>(BehavedBuilder<'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 { if content.is::() { if !self.0.is_empty() { @@ -437,13 +473,16 @@ impl<'a> ParBuilder<'a> { false } + /// Turns this builder into the resulting paragraph, along with + /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { let (children, trunk, span) = self.0.finish(); (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> { /// The list items collected so far. items: BehavedBuilder<'a>, @@ -454,6 +493,12 @@ struct 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 { if !self.items.is_empty() && (content.is::() || content.is::()) @@ -479,6 +524,8 @@ impl<'a> ListBuilder<'a> { false } + /// Turns this builder into the resulting list, along with + /// its [style chain][StyleChain]. fn finish(self) -> (Content, StyleChain<'a>) { let (items, trunk, span) = self.items.finish_iter(); let mut items = items.peekable(); @@ -545,7 +592,7 @@ impl Default for ListBuilder<'_> { } } -/// Accepts citations. +/// Builds a [citation group][CiteGroup] from citations. #[derive(Default)] struct CiteGroupBuilder<'a> { /// The styles. @@ -557,6 +604,12 @@ struct 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 { if !self.items.is_empty() && (content.is::() || content.is::()) @@ -577,6 +630,8 @@ impl<'a> CiteGroupBuilder<'a> { false } + /// Turns this builder into the resulting citation group, along with + /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { let span = self.items.first().map(|cite| cite.span()).unwrap_or(Span::detached()); (Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles) diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs index 8d3d01a61..da06159b0 100644 --- a/crates/typst/src/realize/process.rs +++ b/crates/typst/src/realize/process.rs @@ -15,7 +15,7 @@ use crate::util::{hash128, BitSet}; /// What to do with an element when encountering it during realization. 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). prepared: bool, /// A map of styles to apply to the element. @@ -32,7 +32,7 @@ enum ShowStep<'a> { Builtin, } -/// Whether the `target` element needs processing. +/// Returns whether the `target` element needs processing. pub fn processable<'a>( engine: &mut Engine, target: &'a Content,