diff options
| author | Sébastien d'Herbais de Thun <sebastien.d.herbais@gmail.com> | 2023-11-06 21:37:50 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-06 21:37:50 +0100 |
| commit | c0f6d2004afebfa9412ba0c2d598ef8287197c42 (patch) | |
| tree | 4bb034ca671e7d1982a306f5aecfc4f78a01841d /crates/typst-library/src | |
| parent | 8fd546760c7c425398f0114997c8085a481d8d2a (diff) | |
Content rework 2 - Electric Boogaloo (#2504)
Diffstat (limited to 'crates/typst-library/src')
55 files changed, 492 insertions, 401 deletions
diff --git a/crates/typst-library/src/compute/data.rs b/crates/typst-library/src/compute/data.rs index dadf0bed..e4767ebf 100644 --- a/crates/typst-library/src/compute/data.rs +++ b/crates/typst-library/src/compute/data.rs @@ -66,6 +66,7 @@ pub enum Encoding { } /// A value that can be read from a file. +#[derive(Debug, Clone, PartialEq, Hash)] pub enum Readable { /// A decoded string. Str(Str), diff --git a/crates/typst-library/src/layout/align.rs b/crates/typst-library/src/layout/align.rs index f080f677..9c18266d 100644 --- a/crates/typst-library/src/layout/align.rs +++ b/crates/typst-library/src/layout/align.rs @@ -38,6 +38,9 @@ pub struct AlignElem { impl Show for AlignElem { #[tracing::instrument(name = "AlignElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(Self::set_alignment(self.alignment(styles)))) + Ok(self + .body() + .clone() + .styled(Self::set_alignment(self.alignment(styles)))) } } diff --git a/crates/typst-library/src/layout/enum.rs b/crates/typst-library/src/layout/enum.rs index 436aacb9..8c491dca 100644 --- a/crates/typst-library/src/layout/enum.rs +++ b/crates/typst-library/src/layout/enum.rs @@ -107,6 +107,7 @@ pub struct EnumElem { /// + Numbering! /// ``` #[default(Numbering::Pattern(NumberingPattern::from_str("1.").unwrap()))] + #[borrowed] pub numbering: Numbering, /// Which number to start the enumeration with. @@ -215,7 +216,7 @@ impl Layout for EnumElem { ParElem::leading_in(styles).into() } else { self.spacing(styles) - .unwrap_or_else(|| BlockElem::below_in(styles).amount()) + .unwrap_or_else(|| *BlockElem::below_in(styles).amount()) }; let mut cells = vec![]; @@ -238,7 +239,7 @@ impl Layout for EnumElem { parents.pop(); content } else { - match &numbering { + match numbering { Numbering::Pattern(pattern) => { TextElem::packed(pattern.apply_kth(parents.len(), number)) } @@ -254,7 +255,7 @@ impl Layout for EnumElem { cells.push(Content::empty()); cells.push(resolved); cells.push(Content::empty()); - cells.push(item.body().styled(Self::set_parents(Parent(number)))); + cells.push(item.body().clone().styled(Self::set_parents(Parent(number)))); number = number.saturating_add(1); } @@ -301,6 +302,7 @@ cast! { v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())), } +#[derive(Debug, Clone, Copy, PartialEq, Hash)] struct Parent(usize); cast! { diff --git a/crates/typst-library/src/layout/flow.rs b/crates/typst-library/src/layout/flow.rs index 1feee4b8..5c795c81 100644 --- a/crates/typst-library/src/layout/flow.rs +++ b/crates/typst-library/src/layout/flow.rs @@ -1,5 +1,7 @@ use std::mem; +use comemo::Prehashed; + use super::{ AlignElem, BlockElem, ColbreakElem, ColumnsElem, ParElem, PlaceElem, Spacing, VElem, }; @@ -18,7 +20,7 @@ use crate::visualize::{ pub struct FlowElem { /// The children that will be arranges into a flow. #[variadic] - pub children: Vec<Content>, + pub children: Vec<Prehashed<Content>>, } impl Layout for FlowElem { @@ -37,7 +39,7 @@ impl Layout for FlowElem { } let mut layouter = FlowLayouter::new(regions, styles); - for mut child in &self.children() { + for mut child in self.children().iter().map(|c| &**c) { let outer = styles; let mut styles = styles; if let Some((elem, map)) = child.to_styled() { @@ -199,7 +201,7 @@ impl<'a> FlowLayouter<'a> { rel.resolve(styles).relative_to(self.initial.y), v.weakness(styles) > 0, ), - Spacing::Fr(fr) => FlowItem::Fractional(fr), + Spacing::Fr(fr) => FlowItem::Fractional(*fr), }, ) } @@ -701,7 +703,7 @@ fn find_footnotes(notes: &mut Vec<FootnoteElem>, frame: &Frame) { match item { FrameItem::Group(group) => find_footnotes(notes, &group.frame), FrameItem::Meta(Meta::Elem(content), _) - if !notes.iter().any(|note| note.0.location() == content.location()) => + if !notes.iter().any(|note| note.location() == content.location()) => { let Some(footnote) = content.to::<FootnoteElem>() else { continue }; notes.push(footnote.clone()); diff --git a/crates/typst-library/src/layout/grid.rs b/crates/typst-library/src/layout/grid.rs index 8d015782..b29adeb9 100644 --- a/crates/typst-library/src/layout/grid.rs +++ b/crates/typst-library/src/layout/grid.rs @@ -1,3 +1,5 @@ +use smallvec::{smallvec, SmallVec}; + use crate::prelude::*; use crate::text::TextElem; @@ -66,12 +68,14 @@ pub struct GridElem { /// with that many `{auto}`-sized columns. Note that opposed to rows and /// gutters, providing a single track size will only ever create a single /// column. + #[borrowed] pub columns: TrackSizings, /// The row sizes. /// /// If there are more cells than fit the defined rows, the last row is /// repeated until there are no more cells. + #[borrowed] pub rows: TrackSizings, /// The gaps between rows & columns. @@ -85,10 +89,12 @@ pub struct GridElem { let gutter = args.named("gutter")?; args.named("column-gutter")?.or_else(|| gutter.clone()) )] + #[borrowed] pub column_gutter: TrackSizings, /// The gaps between rows. Takes precedence over `gutter`. #[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))] + #[borrowed] pub row_gutter: TrackSizings, /// The contents of the grid cells. @@ -106,12 +112,16 @@ impl Layout for GridElem { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { + let columns = self.columns(styles); + let rows = self.rows(styles); + let column_gutter = self.column_gutter(styles); + let row_gutter = self.row_gutter(styles); + // Prepare grid layout by unifying content and gutter tracks. - let cells = self.children(); let layouter = GridLayouter::new( - Axes::new(&self.columns(styles).0, &self.rows(styles).0), - Axes::new(&self.column_gutter(styles).0, &self.row_gutter(styles).0), - &cells, + Axes::new(&columns.0, &rows.0), + Axes::new(&column_gutter.0, &row_gutter.0), + &self.children, regions, styles, self.span(), @@ -124,13 +134,13 @@ impl Layout for GridElem { /// Track sizing definitions. #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] -pub struct TrackSizings(pub Vec<Sizing>); +pub struct TrackSizings(pub SmallVec<[Sizing; 4]>); cast! { TrackSizings, self => self.0.into_value(), - sizing: Sizing => Self(vec![sizing]), - count: NonZeroUsize => Self(vec![Sizing::Auto; count.get()]), + sizing: Sizing => Self(smallvec![sizing]), + count: NonZeroUsize => Self(smallvec![Sizing::Auto; count.get()]), values: Array => Self(values.into_iter().map(Value::cast).collect::<StrResult<_>>()?), } diff --git a/crates/typst-library/src/layout/hide.rs b/crates/typst-library/src/layout/hide.rs index 7f17a7d7..af3d0631 100644 --- a/crates/typst-library/src/layout/hide.rs +++ b/crates/typst-library/src/layout/hide.rs @@ -1,3 +1,5 @@ +use smallvec::smallvec; + use crate::prelude::*; /// Hides content without affecting layout. @@ -22,6 +24,6 @@ pub struct HideElem { impl Show for HideElem { #[tracing::instrument(name = "HideElem::show", skip(self))] fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(MetaElem::set_data(vec![Meta::Hide]))) + Ok(self.body().clone().styled(MetaElem::set_data(smallvec![Meta::Hide]))) } } diff --git a/crates/typst-library/src/layout/list.rs b/crates/typst-library/src/layout/list.rs index 34e0e6fb..18c89a24 100644 --- a/crates/typst-library/src/layout/list.rs +++ b/crates/typst-library/src/layout/list.rs @@ -77,6 +77,7 @@ pub struct ListElem { /// - Items /// - Items /// ``` + #[borrowed] #[default(ListMarker::Content(vec![TextElem::packed('•')]))] pub marker: ListMarker, @@ -133,7 +134,7 @@ impl Layout for ListElem { ParElem::leading_in(styles).into() } else { self.spacing(styles) - .unwrap_or_else(|| BlockElem::below_in(styles).amount()) + .unwrap_or_else(|| *BlockElem::below_in(styles).amount()) }; let depth = self.depth(styles); @@ -148,7 +149,7 @@ impl Layout for ListElem { cells.push(Content::empty()); cells.push(marker.clone()); cells.push(Content::empty()); - cells.push(item.body().styled(Self::set_depth(Depth))); + cells.push(item.body().clone().styled(Self::set_depth(Depth))); } let layouter = GridLayouter::new( @@ -183,7 +184,7 @@ cast! { } /// A list's marker. -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum ListMarker { Content(Vec<Content>), Func(Func), @@ -221,6 +222,7 @@ cast! { v: Func => Self::Func(v), } +#[derive(Debug, Clone, Copy, PartialEq, Hash)] struct Depth; cast! { diff --git a/crates/typst-library/src/layout/mod.rs b/crates/typst-library/src/layout/mod.rs index 1c7c2610..d31f6841 100644 --- a/crates/typst-library/src/layout/mod.rs +++ b/crates/typst-library/src/layout/mod.rs @@ -46,6 +46,7 @@ pub use self::table::*; pub use self::terms::*; pub use self::transform::*; +use std::borrow::Cow; use std::mem; use typed_arena::Arena; @@ -242,16 +243,16 @@ fn realize_root<'a>( scratch: &'a Scratch<'a>, content: &'a Content, styles: StyleChain<'a>, -) -> SourceResult<(Content, StyleChain<'a>)> { +) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> { if content.can::<dyn LayoutRoot>() && !applicable(content, styles) { - return Ok((content.clone(), styles)); + return Ok((Cow::Borrowed(content), styles)); } let mut builder = Builder::new(vt, scratch, true); builder.accept(content, styles)?; builder.interrupt_page(Some(styles), true)?; let (pages, shared) = builder.doc.unwrap().pages.finish(); - Ok((DocumentElem::new(pages.to_vec()).pack(), shared)) + Ok((Cow::Owned(DocumentElem::new(pages.to_vec()).pack()), shared)) } /// Realize into an element that is capable of block-level layout. @@ -261,7 +262,7 @@ fn realize_block<'a>( scratch: &'a Scratch<'a>, content: &'a Content, styles: StyleChain<'a>, -) -> SourceResult<(Content, StyleChain<'a>)> { +) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> { // These elements implement `Layout` but still require a flow for // proper layout. if content.can::<dyn Layout>() @@ -277,14 +278,14 @@ fn realize_block<'a>( && !content.is::<PlaceElem>() && !applicable(content, styles) { - return Ok((content.clone(), styles)); + return Ok((Cow::Borrowed(content), styles)); } let mut builder = Builder::new(vt, scratch, false); builder.accept(content, styles)?; builder.interrupt_par()?; let (children, shared) = builder.flow.0.finish(); - Ok((FlowElem::new(children.to_vec()).pack(), shared)) + Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack()), shared)) } /// Builds a document or a flow element from content. @@ -509,7 +510,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { /// Accepts pagebreaks and pages. struct DocBuilder<'a> { /// The page runs built so far. - pages: StyleVecBuilder<'a, Content>, + pages: StyleVecBuilder<'a, Cow<'a, Content>>, /// Whether to keep a following page even if it is empty. keep_next: bool, /// Whether the next page should be cleared to an even or odd number. @@ -517,7 +518,7 @@ struct DocBuilder<'a> { } impl<'a> DocBuilder<'a> { - fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool { + fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool { if let Some(pagebreak) = content.to::<PagebreakElem>() { self.keep_next = !pagebreak.weak(styles); self.clear_next = pagebreak.to(styles); @@ -528,9 +529,9 @@ impl<'a> DocBuilder<'a> { let elem = if let Some(clear_to) = self.clear_next.take() { let mut page = page.clone(); page.push_clear_to(Some(clear_to)); - page.pack() + Cow::Owned(page.pack()) } else { - content.clone() + Cow::Borrowed(content) }; self.pages.push(elem, styles); @@ -571,7 +572,7 @@ impl<'a> FlowBuilder<'a> { || content.is::<MetaElem>() || content.is::<PlaceElem>() { - self.0.push(content.clone(), styles); + self.0.push(Cow::Borrowed(content), styles); return true; } @@ -589,7 +590,7 @@ impl<'a> FlowBuilder<'a> { if !last_was_parbreak && is_tight_list { let leading = ParElem::leading_in(styles); let spacing = VElem::list_attach(leading.into()); - self.0.push(spacing.pack(), styles); + self.0.push(Cow::Owned(spacing.pack()), styles); } let (above, below) = if let Some(block) = content.to::<BlockElem>() { @@ -598,9 +599,9 @@ impl<'a> FlowBuilder<'a> { (BlockElem::above_in(styles), BlockElem::below_in(styles)) }; - self.0.push(above.pack(), styles); - self.0.push(content.clone(), styles); - self.0.push(below.pack(), styles); + self.0.push(Cow::Owned(above.pack()), styles); + self.0.push(Cow::Borrowed(content), styles); + self.0.push(Cow::Owned(below.pack()), styles); return true; } @@ -616,7 +617,7 @@ impl<'a> ParBuilder<'a> { fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool { if content.is::<MetaElem>() { if self.0.has_strong_elements(false) { - self.0.push(content.clone(), styles); + self.0.push(Cow::Borrowed(content), styles); return true; } } else if content.is::<SpaceElem>() @@ -627,7 +628,7 @@ impl<'a> ParBuilder<'a> { || content.to::<EquationElem>().map_or(false, |elem| !elem.block(styles)) || content.is::<BoxElem>() { - self.0.push(content.clone(), styles); + self.0.push(Cow::Borrowed(content), styles); return true; } @@ -643,7 +644,7 @@ impl<'a> ParBuilder<'a> { /// Accepts list / enum items, spaces, paragraph breaks. struct ListBuilder<'a> { /// The list items collected so far. - items: StyleVecBuilder<'a, Content>, + items: StyleVecBuilder<'a, Cow<'a, Content>>, /// Whether the list contains no paragraph breaks. tight: bool, /// Trailing content for which it is unclear whether it is part of the list. @@ -668,7 +669,7 @@ impl<'a> ListBuilder<'a> { .next() .map_or(true, |first| first.func() == content.func()) { - self.items.push(content.clone(), styles); + self.items.push(Cow::Borrowed(content), styles); self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>()); return true; } @@ -685,7 +686,8 @@ impl<'a> ListBuilder<'a> { .iter() .map(|(item, local)| { let item = item.to::<ListItem>().unwrap(); - item.clone().with_body(item.body().styled_with_map(local.clone())) + item.clone() + .with_body(item.body().clone().styled_with_map(local.clone())) }) .collect::<Vec<_>>(), ) @@ -697,7 +699,8 @@ impl<'a> ListBuilder<'a> { .iter() .map(|(item, local)| { let item = item.to::<EnumItem>().unwrap(); - item.clone().with_body(item.body().styled_with_map(local.clone())) + item.clone() + .with_body(item.body().clone().styled_with_map(local.clone())) }) .collect::<Vec<_>>(), ) @@ -710,9 +713,9 @@ impl<'a> ListBuilder<'a> { .map(|(item, local)| { let item = item.to::<TermItem>().unwrap(); item.clone() - .with_term(item.term().styled_with_map(local.clone())) + .with_term(item.term().clone().styled_with_map(local.clone())) .with_description( - item.description().styled_with_map(local.clone()), + item.description().clone().styled_with_map(local.clone()), ) }) .collect::<Vec<_>>(), diff --git a/crates/typst-library/src/layout/page.rs b/crates/typst-library/src/layout/page.rs index 495b390d..5f8f90c1 100644 --- a/crates/typst-library/src/layout/page.rs +++ b/crates/typst-library/src/layout/page.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::ptr; use std::str::FromStr; @@ -172,6 +173,7 @@ pub struct PageElem { /// #set text(fill: rgb("fdfdfd")) /// *Dark mode enabled.* /// ``` + #[borrowed] pub fill: Option<Paint>, /// How to [number]($numbering) the pages. @@ -188,6 +190,7 @@ pub struct PageElem { /// /// #lorem(48) /// ``` + #[borrowed] pub numbering: Option<Numbering>, /// The alignment of the page numbering. @@ -233,6 +236,7 @@ pub struct PageElem { /// /// #lorem(19) /// ``` + #[borrowed] pub header: Option<Content>, /// The amount the header is raised into the top margin. @@ -263,6 +267,7 @@ pub struct PageElem { /// /// #lorem(48) /// ``` + #[borrowed] pub footer: Option<Content>, /// The amount the footer is lowered into the bottom margin. @@ -286,6 +291,7 @@ pub struct PageElem { /// In the year 2023, we plan to take /// over the world (of typesetting). /// ``` + #[borrowed] pub background: Option<Content>, /// Content in the page's foreground. @@ -299,6 +305,7 @@ pub struct PageElem { /// "Weak Reject" because they did /// not understand our approach... /// ``` + #[borrowed] pub foreground: Option<Content>, /// The contents of the page(s). @@ -364,7 +371,7 @@ impl PageElem { }); // Realize columns. - let mut child = self.body(); + let mut child = self.body().clone(); let columns = self.columns(styles); if columns.get() > 1 { child = ColumnsElem::new(child).with_count(columns).pack(); @@ -388,25 +395,25 @@ impl PageElem { } let fill = self.fill(styles); - let foreground = self.foreground(styles); - let background = self.background(styles); + let foreground = Cow::Borrowed(self.foreground(styles)); + let background = Cow::Borrowed(self.background(styles)); let header_ascent = self.header_ascent(styles); let footer_descent = self.footer_descent(styles); let numbering = self.numbering(styles); let numbering_meta = Meta::PageNumbering(numbering.clone().into_value()); let number_align = self.number_align(styles); - let mut header = self.header(styles); - let mut footer = self.footer(styles); + let mut header = Cow::Borrowed(self.header(styles)); + let mut footer = Cow::Borrowed(self.footer(styles)); // Construct the numbering (for header or footer). - let numbering_marginal = numbering.clone().map(|numbering| { - let both = match &numbering { + let numbering_marginal = Cow::Owned(numbering.as_ref().map(|numbering| { + let both = match numbering { Numbering::Pattern(pattern) => pattern.pieces() >= 2, Numbering::Func(_) => true, }; let mut counter = - Counter::new(CounterKey::Page).display(Some(numbering), both); + Counter::new(CounterKey::Page).display(Some(numbering.clone()), both); // We interpret the Y alignment as selecting header or footer // and then ignore it for aligning the actual number. @@ -415,12 +422,12 @@ impl PageElem { } counter - }); + })); if matches!(number_align.y(), Some(VAlign::Top)) { - header = header.or(numbering_marginal); + header = if header.is_some() { header } else { numbering_marginal }; } else { - footer = footer.or(numbering_marginal); + footer = if footer.is_some() { footer } else { numbering_marginal }; } // Post-process pages. @@ -455,7 +462,7 @@ impl PageElem { ] { tracing::info!("Layouting {name}"); - let Some(content) = marginal else { continue }; + let Some(content) = &**marginal else { continue }; let (pos, area, align); if ptr::eq(marginal, &header) { @@ -488,14 +495,14 @@ impl PageElem { } } - if let Some(fill) = &fill { + if let Some(fill) = fill { frame.fill(fill.clone()); } page_counter.visit(vt, frame)?; // Add a PDF page label if there is a numbering. - if let Some(num) = &numbering { + if let Some(num) = numbering { if let Some(page_label) = num.apply_pdf(page_counter.logical()) { frame.push_positionless_meta(Meta::PdfPageLabel(page_label)); } @@ -657,10 +664,10 @@ pub enum Marginal { impl Marginal { /// Resolve the marginal based on the page number. - pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Content> { + pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Cow<'_, Content>> { Ok(match self { - Self::Content(content) => content.clone(), - Self::Func(func) => func.call_vt(vt, [page])?.display(), + Self::Content(content) => Cow::Borrowed(content), + Self::Func(func) => Cow::Owned(func.call_vt(vt, [page])?.display()), }) } } diff --git a/crates/typst-library/src/layout/par.rs b/crates/typst-library/src/layout/par.rs index 10f78ef2..b3d5fb3e 100644 --- a/crates/typst-library/src/layout/par.rs +++ b/crates/typst-library/src/layout/par.rs @@ -1,3 +1,4 @@ +use comemo::Prehashed; use typst::eval::Tracer; use typst::model::DelayedErrors; use unicode_bidi::{BidiInfo, Level as BidiLevel}; @@ -104,7 +105,7 @@ pub struct ParElem { /// The paragraph's children. #[internal] #[variadic] - pub children: Vec<Content>, + pub children: Vec<Prehashed<Content>>, } impl Construct for ParElem { @@ -158,12 +159,12 @@ impl ParElem { let children = par.children(); // Collect all text into one string for BiDi analysis. - let (text, segments, spans) = collect(&children, &styles, consecutive)?; + let (text, segments, spans) = collect(children, &styles, consecutive)?; // Perform BiDi analysis and then prepare paragraph layout by building a // representation on which we can do line breaking without layouting // each and every line from scratch. - let p = prepare(&mut vt, &children, &text, segments, spans, styles, region)?; + let p = prepare(&mut vt, children, &text, segments, spans, styles, region)?; // Break the paragraph into lines. let lines = linebreak(&vt, &p, region.x - p.hang); @@ -246,8 +247,6 @@ pub(crate) struct Preparation<'a> { pub items: Vec<Item<'a>>, /// The span mapper. pub spans: SpanMapper, - /// The styles shared by all children. - pub styles: StyleChain<'a>, /// Whether to hyphenate if it's the same for all children. pub hyphenate: Option<bool>, /// The text language if it's the same for all children. @@ -258,8 +257,16 @@ pub(crate) struct Preparation<'a> { pub justify: bool, /// The paragraph's hanging indent. pub hang: Abs, - /// The CJK-latin spacing. + /// Whether to add spacing between CJK and Latin characters. pub cjk_latin_spacing: bool, + /// Whether font fallback is enabled for this paragraph. + pub fallback: bool, + /// The leading of the paragraph. + pub leading: Abs, + /// How to determine line breaks. + pub linebreaks: Smart<Linebreaks>, + /// The text size. + pub size: Abs, } impl<'a> Preparation<'a> { @@ -525,15 +532,15 @@ impl<'a> Line<'a> { /// string-level preprocessing like case transformations. #[allow(clippy::type_complexity)] fn collect<'a>( - children: &'a [Content], + children: &'a [Prehashed<Content>], styles: &'a StyleChain<'a>, consecutive: bool, ) -> SourceResult<(String, Vec<(Segment<'a>, StyleChain<'a>)>, SpanMapper)> { let mut full = String::new(); let mut quoter = Quoter::new(); - let mut segments = vec![]; + let mut segments = Vec::with_capacity(2 + children.len()); let mut spans = SpanMapper::new(); - let mut iter = children.iter().peekable(); + let mut iter = children.iter().map(|c| &**c).peekable(); let first_line_indent = ParElem::first_line_indent_in(*styles); if !first_line_indent.is_zero() @@ -565,9 +572,9 @@ fn collect<'a>( } else if let Some(elem) = child.to::<TextElem>() { let prev = full.len(); if let Some(case) = TextElem::case_in(styles) { - full.push_str(&case.apply(&elem.text())); + full.push_str(&case.apply(elem.text())); } else { - full.push_str(&elem.text()); + full.push_str(elem.text()); } Segment::Text(full.len() - prev) } else if let Some(elem) = child.to::<HElem>() { @@ -576,7 +583,7 @@ fn collect<'a>( } full.push(SPACING_REPLACE); - Segment::Spacing(elem.amount()) + Segment::Spacing(*elem.amount()) } else if let Some(elem) = child.to::<LinebreakElem>() { let c = if elem.justify(styles) { '\u{2028}' } else { '\n' }; full.push(c); @@ -588,7 +595,7 @@ fn collect<'a>( let lang = TextElem::lang_in(styles); let region = TextElem::region_in(styles); let quotes = Quotes::new( - "es, + quotes, lang, region, SmartquoteElem::alternative_in(styles), @@ -656,7 +663,7 @@ fn collect<'a>( /// contained inline-level content. fn prepare<'a>( vt: &mut Vt, - children: &'a [Content], + children: &'a [Prehashed<Content>], text: &'a str, segments: Vec<(Segment<'a>, StyleChain<'a>)>, spans: SpanMapper, @@ -674,7 +681,7 @@ fn prepare<'a>( ); let mut cursor = 0; - let mut items = vec![]; + let mut items = Vec::with_capacity(segments.len()); // Shape / layout the children and collect them into items. for (segment, styles) in segments { @@ -727,13 +734,16 @@ fn prepare<'a>( bidi, items, spans, - styles, hyphenate: shared_get(styles, children, TextElem::hyphenate_in), lang: shared_get(styles, children, TextElem::lang_in), align: AlignElem::alignment_in(styles).resolve(styles).x, justify: ParElem::justify_in(styles), hang: ParElem::hanging_indent_in(styles), cjk_latin_spacing, + fallback: TextElem::fallback_in(styles), + leading: ParElem::leading_in(styles), + linebreaks: ParElem::linebreaks_in(styles), + size: TextElem::size_in(styles), }) } @@ -852,7 +862,7 @@ fn is_compatible(a: Script, b: Script) -> bool { /// paragraph. fn shared_get<T: PartialEq>( styles: StyleChain<'_>, - children: &[Content], + children: &[Prehashed<Content>], getter: fn(StyleChain) -> T, ) -> Option<T> { let value = getter(styles); @@ -865,8 +875,8 @@ fn shared_get<T: PartialEq>( /// Find suitable linebreaks. fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { - let linebreaks = ParElem::linebreaks_in(p.styles).unwrap_or_else(|| { - if ParElem::justify_in(p.styles) { + let linebreaks = p.linebreaks.unwrap_or_else(|| { + if p.justify { Linebreaks::Optimized } else { Linebreaks::Simple @@ -883,7 +893,7 @@ fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { /// lines greedily, always taking the longest possible line. This may lead to /// very unbalanced line, but is fast and simple. fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { - let mut lines = vec![]; + let mut lines = Vec::with_capacity(16); let mut start = 0; let mut last = None; @@ -964,8 +974,8 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L line: line(vt, p, 0..0, Breakpoint::Mandatory), }]; - let em = TextElem::size_in(p.styles); - + let em = p.size; + let mut lines = Vec::with_capacity(16); breakpoints(p, |end, breakpoint| { let k = table.len(); let eof = end == p.bidi.text.len(); @@ -1071,7 +1081,6 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L }); // Retrace the best path. - let mut lines = vec![]; let mut idx = table.len() - 1; while idx != 0 { table.truncate(idx + 1); @@ -1151,7 +1160,7 @@ fn line<'a>( if hyphen || start < range.end || before.is_empty() { let mut reshaped = shaped.reshape(vt, &p.spans, start..range.end); if hyphen || shy { - reshaped.push_hyphen(vt, TextElem::fallback_in(p.styles)); + reshaped.push_hyphen(vt, p.fallback); } if let Some(last_glyph) = reshaped.glyphs.last() { @@ -1287,11 +1296,10 @@ fn finalize( .collect::<SourceResult<_>>()?; // Prevent orphans. - let leading = ParElem::leading_in(p.styles); if frames.len() >= 2 && !frames[1].is_empty() { let second = frames.remove(1); let first = &mut frames[0]; - merge(first, second, leading); + merge(first, second, p.leading); } // Prevent widows. @@ -1299,7 +1307,7 @@ fn finalize( if len >= 2 && !frames[len - 2].is_empty() { let second = frames.pop().unwrap(); let first = frames.last_mut().unwrap(); - merge(first, second, leading); + merge(first, second, p.leading); } Ok(Fragment::frames(frames)) diff --git a/crates/typst-library/src/layout/place.rs b/crates/typst-library/src/layout/place.rs index 64cbd9a8..c8e83383 100644 --- a/crates/typst-library/src/layout/place.rs +++ b/crates/typst-library/src/layout/place.rs @@ -106,7 +106,10 @@ impl Layout for PlaceElem { .at(self.span()); } - let child = self.body().aligned(alignment.unwrap_or_else(|| Align::CENTER)); + let child = self + .body() + .clone() + .aligned(alignment.unwrap_or_else(|| Align::CENTER)); let pod = Regions::one(base, Axes::splat(false)); let frame = child.layout(vt, styles, pod)?.into_frame(); diff --git a/crates/typst-library/src/layout/spacing.rs b/crates/typst-library/src/layout/spacing.rs index 37f1ed35..88b6e2cd 100644 --- a/crates/typst-library/src/layout/spacing.rs +++ b/crates/typst-library/src/layout/spacing.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::prelude::*; /// Inserts horizontal spacing into a paragraph. @@ -71,7 +73,7 @@ impl Behave for HElem { fn larger( &self, - prev: &(Content, Behaviour, StyleChain), + prev: &(Cow<Content>, Behaviour, StyleChain), styles: StyleChain, ) -> bool { let Some(other) = prev.0.to::<Self>() else { return false }; @@ -173,7 +175,7 @@ impl Behave for VElem { fn larger( &self, - prev: &(Content, Behaviour, StyleChain), + prev: &(Cow<Content>, Behaviour, StyleChain), styles: StyleChain, ) -> bool { let Some(other) = prev.0.to::<Self>() else { return false }; diff --git a/crates/typst-library/src/layout/stack.rs b/crates/typst-library/src/layout/stack.rs index 398341b2..50d1c862 100644 --- a/crates/typst-library/src/layout/stack.rs +++ b/crates/typst-library/src/layout/stack.rs @@ -60,7 +60,7 @@ impl Layout for StackElem { for child in self.children() { match child { StackChild::Spacing(kind) => { - layouter.layout_spacing(kind); + layouter.layout_spacing(*kind); deferred = None; } StackChild::Block(block) => { @@ -68,7 +68,7 @@ impl Layout for StackElem { layouter.layout_spacing(kind); } - layouter.layout_block(vt, &block, styles)?; + layouter.layout_block(vt, block, styles)?; deferred = spacing; } } @@ -79,7 +79,7 @@ impl Layout for StackElem { } /// A child of a stack element. -#[derive(Hash)] +#[derive(Clone, PartialEq, Hash)] pub enum StackChild { /// Spacing between other children. Spacing(Spacing), diff --git a/crates/typst-library/src/layout/table.rs b/crates/typst-library/src/layout/table.rs index 056b63a2..9e7da071 100644 --- a/crates/typst-library/src/layout/table.rs +++ b/crates/typst-library/src/layout/table.rs @@ -1,7 +1,7 @@ use typst::eval::{CastInfo, Reflect}; use crate::layout::{AlignElem, GridLayouter, TrackSizings}; -use crate::meta::{Figurable, LocalName}; +use crate::meta::Figurable; use crate::prelude::*; /// A table of items. @@ -38,10 +38,12 @@ use crate::prelude::*; pub struct TableElem { /// The column sizes. See the [grid documentation]($grid) for more /// information on track sizing. + #[borrowed] pub columns: TrackSizings, /// The row sizes. See the [grid documentation]($grid) for more information /// on track sizing. + #[borrowed] pub rows: TrackSizings, /// The gaps between rows & columns. See the [grid documentation]($grid) for @@ -51,6 +53,7 @@ pub struct TableElem { /// The gaps between columns. Takes precedence over `gutter`. See the /// [grid documentation]($grid) for more information on gutters. + #[borrowed] #[parse( let gutter = args.named("gutter")?; args.named("column-gutter")?.or_else(|| gutter.clone()) @@ -60,6 +63,7 @@ pub struct TableElem { /// The gaps between rows. Takes precedence over `gutter`. See the /// [grid documentation]($grid) for more information on gutters. #[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))] + #[borrowed] pub row_gutter: TrackSizings, /// How to fill the cells. @@ -82,6 +86,7 @@ pub struct TableElem { /// [Profit:], [500 €], [1000 €], [1500 €], /// ) /// ``` + #[borrowed] pub fill: Celled<Option<Paint>>, /// How to align the cells' content. @@ -99,6 +104,7 @@ pub struct TableElem { /// [A], [B], [C], /// ) /// ``` + #[borrowed] pub align: Celled<Smart<Align>>, /// How to [stroke]($stroke) the cells. @@ -151,16 +157,20 @@ impl Layout for TableElem { ) -> SourceResult<Fragment> { let inset = self.inset(styles); let align = self.align(styles); + let columns = self.columns(styles); + let rows = self.rows(styles); + let column_gutter = self.column_gutter(styles); + let row_gutter = self.row_gutter(styles); - let tracks = Axes::new(self.columns(styles).0, self.rows(styles).0); - let gutter = Axes::new(self.column_gutter(styles).0, self.row_gutter(styles).0); + let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice()); + let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice()); let cols = tracks.x.len().max(1); let cells: Vec<_> = self .children() - .into_iter() + .iter() .enumerate() .map(|(i, child)| { - let mut child = child.padded(inset); + let mut child = child.clone().padded(inset); let x = i % cols; let y = i / cols; @@ -176,14 +186,8 @@ impl Layout for TableElem { let stroke = self.stroke(styles).map(Stroke::unwrap_or_default); // Prepare grid layout by unifying content and gutter tracks. - let layouter = GridLayouter::new( - tracks.as_deref(), - gutter.as_deref(), - &cells, - regions, - styles, - self.span(), - ); + let layouter = + GridLayouter::new(tracks, gutter, &cells, regions, styles, self.span()); // Measure the columns and layout the grid row-by-row. let mut layout = layouter.layout(vt)?; @@ -321,7 +325,7 @@ impl<T: FromValue> FromValue for Celled<T> { } impl LocalName for TableElem { - fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str { + fn local_name(lang: Lang, _: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Tabel", Lang::ARABIC => "جدول", diff --git a/crates/typst-library/src/layout/terms.rs b/crates/typst-library/src/layout/terms.rs index 07f17bb0..d4262118 100644 --- a/crates/typst-library/src/layout/terms.rs +++ b/crates/typst-library/src/layout/terms.rs @@ -55,6 +55,7 @@ pub struct TermsElem { /// / Colon: A nice separator symbol. /// ``` #[default(HElem::new(Em::new(0.6).into()).with_weak(true).pack())] + #[borrowed] pub separator: Content, /// The indentation of each item. @@ -114,20 +115,20 @@ impl Layout for TermsElem { ParElem::leading_in(styles).into() } else { self.spacing(styles) - .unwrap_or_else(|| BlockElem::below_in(styles).amount()) + .unwrap_or_else(|| *BlockElem::below_in(styles).amount()) }; let mut seq = vec![]; - for (i, child) in self.children().into_iter().enumerate() { + for (i, child) in self.children().iter().enumerate() { if i > 0 { seq.push(VElem::new(gutter).with_weakness(1).pack()); } if !indent.is_zero() { seq.push(HElem::new(indent.into()).pack()); } - seq.push(child.term().strong()); - seq.push(separator.clone()); - seq.push(child.description()); + seq.push(child.term().clone().strong()); + seq.push((*separator).clone()); + seq.push(child.description().clone()); } Content::sequence(seq) diff --git a/crates/typst-library/src/math/accent.rs b/crates/typst-library/src/math/accent.rs index a542111b..bedc5635 100644 --- a/crates/typst-library/src/math/accent.rs +++ b/crates/typst-library/src/math/accent.rs @@ -52,7 +52,7 @@ impl LayoutMath for AccentElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { ctx.style(ctx.style.with_cramped(true)); - let base = ctx.layout_fragment(&self.base())?; + let base = ctx.layout_fragment(self.base())?; ctx.unstyle(); // Preserve class to preserve automatic spacing. @@ -67,7 +67,7 @@ impl LayoutMath for AccentElem { // Forcing the accent to be at least as large as the base makes it too // wide in many case. let Accent(c) = self.accent(); - let glyph = GlyphFragment::new(ctx, c, self.span()); + let glyph = GlyphFragment::new(ctx, *c, self.span()); let short_fall = ACCENT_SHORT_FALL.scaled(ctx); let variant = glyph.stretch_horizontal(ctx, base.width(), short_fall); let accent = variant.frame; @@ -116,6 +116,7 @@ fn attachment(ctx: &MathContext, id: GlyphId, italics_correction: Abs) -> Abs { } /// An accent character. +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] pub struct Accent(char); impl Accent { @@ -130,7 +131,7 @@ cast! { self => self.0.into_value(), v: char => Self::new(v), v: Content => match v.to::<TextElem>() { - Some(elem) => Value::Str(elem.text().into()).cast()?, + Some(elem) => Value::Str(elem.text().clone().into()).cast()?, None => bail!("expected text"), }, } diff --git a/crates/typst-library/src/math/attach.rs b/crates/typst-library/src/math/attach.rs index 5d1e477e..3e6b69f2 100644 --- a/crates/typst-library/src/math/attach.rs +++ b/crates/typst-library/src/math/attach.rs @@ -50,7 +50,7 @@ impl LayoutMath for AttachElem { .transpose() }; - let base = ctx.layout_fragment(&self.base())?; + let base = ctx.layout_fragment(self.base())?; ctx.style(ctx.style.for_superscript()); let tl = layout_attachment(ctx, Self::tl)?; @@ -91,7 +91,7 @@ pub struct PrimesElem { impl LayoutMath for PrimesElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - match self.count() { + match *self.count() { count @ 1..=4 => { let f = ctx.layout_fragment(&TextElem::packed(match count { 1 => '′', @@ -137,7 +137,7 @@ pub struct ScriptsElem { impl LayoutMath for ScriptsElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let mut fragment = ctx.layout_fragment(&self.body())?; + let mut fragment = ctx.layout_fragment(self.body())?; fragment.set_limits(Limits::Never); ctx.push(fragment); Ok(()) @@ -166,7 +166,7 @@ pub struct LimitsElem { impl LayoutMath for LimitsElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let mut fragment = ctx.layout_fragment(&self.body())?; + let mut fragment = ctx.layout_fragment(self.body())?; fragment.set_limits(if self.inline(ctx.styles()) { Limits::Always } else { diff --git a/crates/typst-library/src/math/cancel.rs b/crates/typst-library/src/math/cancel.rs index d521149e..16e4067a 100644 --- a/crates/typst-library/src/math/cancel.rs +++ b/crates/typst-library/src/math/cancel.rs @@ -97,7 +97,7 @@ pub struct CancelElem { impl LayoutMath for CancelElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let body = ctx.layout_fragment(&self.body())?; + let body = ctx.layout_fragment(self.body())?; // Use the same math class as the body, in order to preserve automatic spacing around it. let body_class = body.class().unwrap_or(MathClass::Special); let mut body = body.into_frame(); @@ -146,6 +146,7 @@ impl LayoutMath for CancelElem { } /// Defines the cancel line. +#[derive(Debug, Clone, PartialEq, Hash)] pub enum CancelAngle { Angle(Angle), Func(Func), diff --git a/crates/typst-library/src/math/class.rs b/crates/typst-library/src/math/class.rs index fc8a6c79..d2c5192d 100644 --- a/crates/typst-library/src/math/class.rs +++ b/crates/typst-library/src/math/class.rs @@ -27,11 +27,11 @@ pub struct ClassElem { impl LayoutMath for ClassElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - ctx.style(ctx.style.with_class(self.class())); - let mut fragment = ctx.layout_fragment(&self.body())?; + ctx.style(ctx.style.with_class(*self.class())); + let mut fragment = ctx.layout_fragment(self.body())?; ctx.unstyle(); - fragment.set_class(self.class()); + fragment.set_class(*self.class()); ctx.push(fragment); Ok(()) } diff --git a/crates/typst-library/src/math/ctx.rs b/crates/typst-library/src/math/ctx.rs index 59ba057f..2637921c 100644 --- a/crates/typst-library/src/math/ctx.rs +++ b/crates/typst-library/src/math/ctx.rs @@ -1,3 +1,4 @@ +use comemo::Prehashed; use ttf_parser::gsub::SubstitutionSubtable; use ttf_parser::math::MathValue; use typst::font::{FontStyle, FontWeight}; @@ -251,7 +252,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { // because it will be placed somewhere probably not at the left margin // it will overflow. So emulate an `hbox` instead and allow the paragraph // to extend as far as needed. - let frame = ParElem::new(vec![elem]) + let frame = ParElem::new(vec![Prehashed::new(elem)]) .layout( self.vt, self.outer.chain(&self.local), diff --git a/crates/typst-library/src/math/frac.rs b/crates/typst-library/src/math/frac.rs index ee582775..bd8d86bf 100644 --- a/crates/typst-library/src/math/frac.rs +++ b/crates/typst-library/src/math/frac.rs @@ -29,7 +29,7 @@ pub struct FracElem { impl LayoutMath for FracElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.num(), &[self.denom()], false, self.span()) + layout(ctx, self.num(), std::slice::from_ref(self.denom()), false, self.span()) } } @@ -62,7 +62,7 @@ pub struct BinomElem { impl LayoutMath for BinomElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.upper(), &self.lower(), true, self.span()) + layout(ctx, self.upper(), self.lower(), true, self.span()) } } diff --git a/crates/typst-library/src/math/fragment.rs b/crates/typst-library/src/math/fragment.rs index 71d8d237..a40ed2bf 100644 --- a/crates/typst-library/src/math/fragment.rs +++ b/crates/typst-library/src/math/fragment.rs @@ -196,7 +196,7 @@ pub struct GlyphFragment { pub font_size: Abs, pub class: Option<MathClass>, pub span: Span, - pub meta: Vec<Meta>, + pub meta: SmallVec<[Meta; 1]>, pub limits: Limits, } diff --git a/crates/typst-library/src/math/lr.rs b/crates/typst-library/src/math/lr.rs index 16696f2e..39143620 100644 --- a/crates/typst-library/src/math/lr.rs +++ b/crates/typst-library/src/math/lr.rs @@ -37,7 +37,7 @@ impl LayoutMath for LrElem { } } - let mut fragments = ctx.layout_fragments(&body)?; + let mut fragments = ctx.layout_fragments(body)?; let axis = scaled!(ctx, axis_height); let max_extent = fragments .iter() diff --git a/crates/typst-library/src/math/matrix.rs b/crates/typst-library/src/math/matrix.rs index 333a5706..b54da5d6 100644 --- a/crates/typst-library/src/math/matrix.rs +++ b/crates/typst-library/src/math/matrix.rs @@ -49,7 +49,7 @@ impl LayoutMath for VecElem { let delim = self.delim(ctx.styles()); let frame = layout_vec_body( ctx, - &self.children(), + self.children(), FixedAlign::Center, self.gap(ctx.styles()), )?; @@ -233,7 +233,7 @@ impl LayoutMath for MatElem { let delim = self.delim(ctx.styles()); let frame = layout_mat_body( ctx, - &rows, + rows, augment, Axes::new(self.column_gap(ctx.styles()), self.row_gap(ctx.styles())), self.span(), @@ -303,7 +303,7 @@ impl LayoutMath for CasesElem { let delim = self.delim(ctx.styles()); let frame = layout_vec_body( ctx, - &self.children(), + self.children(), FixedAlign::Start, self.gap(ctx.styles()), )?; @@ -562,7 +562,7 @@ fn layout_delimiters( /// Parameters specifying how augmentation lines /// should be drawn on a matrix. -#[derive(Default, Clone, Hash)] +#[derive(Debug, Default, Clone, PartialEq, Hash)] pub struct Augment<T: Numeric = Length> { pub hline: Offsets, pub vline: Offsets, @@ -573,7 +573,7 @@ impl Augment<Abs> { fn stroke_or(&self, fallback: FixedStroke) -> FixedStroke { match &self.stroke { Smart::Custom(v) => v.clone().unwrap_or(fallback), - _ => fallback, + Smart::Auto => fallback, } } } @@ -594,7 +594,13 @@ impl Fold for Augment<Abs> { type Output = Augment<Abs>; fn fold(mut self, outer: Self::Output) -> Self::Output { - self.stroke = self.stroke.fold(outer.stroke); + // Special case for handling `auto` strokes in subsequent `Augment`. + if self.stroke.is_auto() && outer.stroke.is_custom() { + self.stroke = outer.stroke; + } else { + self.stroke = self.stroke.fold(outer.stroke); + } + self } } @@ -602,19 +608,22 @@ impl Fold for Augment<Abs> { cast! { Augment, self => { - let stroke = self.stroke.unwrap_or_default(); + // if the stroke is auto and there is only one vertical line, + if self.stroke.is_auto() && self.hline.0.is_empty() && self.vline.0.len() == 1 { + return self.vline.0[0].into_value(); + } let d = dict! { "hline" => self.hline.into_value(), "vline" => self.vline.into_value(), - "stroke" => stroke.into_value() + "stroke" => self.stroke.into_value() }; d.into_value() }, v: isize => Augment { hline: Offsets::default(), - vline: Offsets(vec![v]), + vline: Offsets(smallvec![v]), stroke: Smart::Auto, }, mut dict: Dict => { @@ -636,14 +645,13 @@ cast! { self => self.into_value(), } -/// The offsets at which augmentation lines -/// should be drawn on a matrix. -#[derive(Debug, Default, Clone, Hash)] -pub struct Offsets(Vec<isize>); +/// The offsets at which augmentation lines should be drawn on a matrix. +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +pub struct Offsets(SmallVec<[isize; 1]>); cast! { Offsets, self => self.0.into_value(), - v: isize => Self(vec![v]), + v: isize => Self(smallvec![v]), v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?), } diff --git a/crates/typst-library/src/math/mod.rs b/crates/typst-library/src/math/mod.rs index 26ab10c6..a4d44d00 100644 --- a/crates/typst-library/src/math/mod.rs +++ b/crates/typst-library/src/math/mod.rs @@ -32,6 +32,8 @@ pub use self::root::*; pub use self::style::*; pub use self::underover::*; +use std::borrow::Cow; + use ttf_parser::{GlyphId, Rect}; use typst::eval::{Module, Scope}; use typst::font::{Font, FontWeight}; @@ -46,7 +48,7 @@ use self::spacing::*; use crate::layout::{AlignElem, BoxElem, HElem, ParElem, Spacing}; use crate::meta::Supplement; use crate::meta::{ - Count, Counter, CounterUpdate, LocalName, Numbering, Outlinable, Refable, + Count, Counter, CounterUpdate, LocalNameIn, Numbering, Outlinable, Refable, }; use crate::prelude::*; use crate::shared::BehavedBuilder; @@ -182,7 +184,7 @@ impl Synthesize for EquationElem { fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> { // Resolve the supplement. let supplement = match self.supplement(styles) { - Smart::Auto => TextElem::packed(self.local_name_in(styles)), + Smart::Auto => TextElem::packed(Self::local_name_in(styles)), Smart::Custom(None) => Content::empty(), Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?, }; @@ -236,7 +238,7 @@ impl Layout for EquationElem { let variant = variant(styles); let world = vt.world; let Some(font) = families(styles).find_map(|family| { - let id = world.book().select(family.as_str(), variant)?; + let id = world.book().select(family, variant)?; let font = world.font(id)?; let _ = font.ttf().tables().math?.constants?; Some(font) @@ -312,7 +314,7 @@ impl Count for EquationElem { } impl LocalName for EquationElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Ekuacion", Lang::ARABIC => "معادلة", @@ -381,7 +383,7 @@ impl Outlinable for EquationElem { let numbers = self .counter() - .at(vt, self.0.location().unwrap())? + .at(vt, self.location().unwrap())? .display(vt, &numbering)?; Ok(Some(supplement + numbers)) @@ -419,7 +421,7 @@ impl LayoutMath for Content { if self.is_sequence() { let mut bb = BehavedBuilder::new(); self.sequence_recursive_for_each(&mut |child: &Content| { - bb.push(child.clone(), StyleChain::default()) + bb.push(Cow::Owned(child.clone()), StyleChain::default()) }); for (child, _) in bb.finish().0.iter() { diff --git a/crates/typst-library/src/math/op.rs b/crates/typst-library/src/math/op.rs index 152efdf5..b4c36002 100644 --- a/crates/typst-library/src/math/op.rs +++ b/crates/typst-library/src/math/op.rs @@ -32,7 +32,7 @@ impl LayoutMath for OpElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let fragment = - ctx.layout_text(&TextElem::new(self.text()).spanned(self.span()))?; + ctx.layout_text(&TextElem::new(self.text().clone()).spanned(self.span()))?; ctx.push( FrameFragment::new(ctx, fragment.into_frame()) .with_class(MathClass::Large) diff --git a/crates/typst-library/src/math/root.rs b/crates/typst-library/src/math/root.rs index 49ceb75f..13c5c147 100644 --- a/crates/typst-library/src/math/root.rs +++ b/crates/typst-library/src/math/root.rs @@ -32,7 +32,7 @@ pub struct RootElem { impl LayoutMath for RootElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, self.index(ctx.styles()).as_ref(), &self.radicand(), self.span()) + layout(ctx, self.index(ctx.styles()).as_ref(), self.radicand(), self.span()) } } diff --git a/crates/typst-library/src/math/style.rs b/crates/typst-library/src/math/style.rs index 4d80a235..774fadac 100644 --- a/crates/typst-library/src/math/style.rs +++ b/crates/typst-library/src/math/style.rs @@ -344,7 +344,7 @@ impl MathStyle { /// The size of elements in an equation. /// /// See the TeXbook p. 141. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Cast)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Cast, Hash)] pub enum MathSize { /// Second-level sub- and superscripts. ScriptScript, @@ -367,7 +367,7 @@ impl MathSize { } /// A mathematical style variant, as defined by Unicode. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Cast)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Cast, Hash)] pub enum MathVariant { Serif, Sans, diff --git a/crates/typst-library/src/math/underover.rs b/crates/typst-library/src/math/underover.rs index 5d010c28..aeb83061 100644 --- a/crates/typst-library/src/math/underover.rs +++ b/crates/typst-library/src/math/underover.rs @@ -24,7 +24,7 @@ pub struct UnderlineElem { impl LayoutMath for UnderlineElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout_underoverline(ctx, &self.body(), self.span(), LineKind::Under) + layout_underoverline(ctx, self.body(), self.span(), LineKind::Under) } } @@ -43,7 +43,7 @@ pub struct OverlineElem { impl LayoutMath for OverlineElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout_underoverline(ctx, &self.body(), self.span(), LineKind::Over) + layout_underoverline(ctx, self.body(), self.span(), LineKind::Over) } } @@ -130,7 +130,7 @@ impl LayoutMath for UnderbraceElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout_underoverspreader( ctx, - &self.body(), + self.body(), &self.annotation(ctx.styles()), '⏟', BRACE_GAP, @@ -161,7 +161,7 @@ impl LayoutMath for OverbraceElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout_underoverspreader( ctx, - &self.body(), + self.body(), &self.annotation(ctx.styles()), '⏞', BRACE_GAP, @@ -192,7 +192,7 @@ impl LayoutMath for UnderbracketElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout_underoverspreader( ctx, - &self.body(), + self.body(), &self.annotation(ctx.styles()), '⎵', BRACKET_GAP, @@ -223,7 +223,7 @@ impl LayoutMath for OverbracketElem { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout_underoverspreader( ctx, - &self.body(), + self.body(), &self.annotation(ctx.styles()), '⎴', BRACKET_GAP, diff --git a/crates/typst-library/src/meta/bibliography.rs b/crates/typst-library/src/meta/bibliography.rs index d429d343..342135ec 100644 --- a/crates/typst-library/src/meta/bibliography.rs +++ b/crates/typst-library/src/meta/bibliography.rs @@ -19,13 +19,13 @@ use typed_arena::Arena; use typst::diag::FileError; use typst::eval::{eval_string, Bytes, CastInfo, EvalMode, Reflect}; use typst::font::FontStyle; -use typst::util::option_eq; +use typst::util::{option_eq, PicoStr}; use super::{CitationForm, CiteGroup, LocalName}; use crate::layout::{ BlockElem, GridElem, HElem, PadElem, ParElem, Sizing, TrackSizings, VElem, }; -use crate::meta::{FootnoteElem, HeadingElem}; +use crate::meta::{FootnoteElem, HeadingElem, LocalNameIn}; use crate::prelude::*; use crate::text::{Delta, SubElem, SuperElem, TextElem}; @@ -130,7 +130,7 @@ pub struct BibliographyElem { } /// A list of bibliography file paths. -#[derive(Debug, Default, Clone, Hash)] +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct BibPaths(Vec<EcoString>); cast! { @@ -153,11 +153,12 @@ impl BibliographyElem { bail!("multiple bibliographies are not yet supported"); } - Ok(elem.to::<Self>().unwrap().clone()) + Ok(elem.to::<Self>().cloned().unwrap()) } /// Whether the bibliography contains the given key. - pub fn has(vt: &Vt, key: &str) -> bool { + pub fn has(vt: &Vt, key: impl Into<PicoStr>) -> bool { + let key = key.into(); vt.introspector .query(&Self::elem().select()) .iter() @@ -199,14 +200,9 @@ impl Show for BibliographyElem { let mut seq = vec![]; if let Some(title) = self.title(styles) { - let title = - title.unwrap_or_else(|| { - TextElem::packed(self.local_name( - TextElem::lang_in(styles), - TextElem::region_in(styles), - )) - .spanned(self.span()) - }); + let title = title.unwrap_or_else(|| { + TextElem::packed(Self::local_name_in(styles)).spanned(self.span()) + }); seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack()); } @@ -220,7 +216,7 @@ impl Show for BibliographyElem { .ok_or("CSL style is not suitable for bibliographies") .at(span)?; - let row_gutter = BlockElem::below_in(styles).amount(); + let row_gutter = *BlockElem::below_in(styles).amount(); if references.iter().any(|(prefix, _)| prefix.is_some()) { let mut cells = vec![]; for (prefix, reference) in references { @@ -231,9 +227,9 @@ impl Show for BibliographyElem { seq.push(VElem::new(row_gutter).with_weakness(3).pack()); seq.push( GridElem::new(cells) - .with_columns(TrackSizings(vec![Sizing::Auto; 2])) - .with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()])) - .with_row_gutter(TrackSizings(vec![row_gutter.into()])) + .with_columns(TrackSizings(smallvec![Sizing::Auto; 2])) + .with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()])) + .with_row_gutter(TrackSizings(smallvec![(row_gutter).into()])) .pack(), ); } else { @@ -263,7 +259,7 @@ impl Finalize for BibliographyElem { } impl LocalName for BibliographyElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Bibliografi", Lang::ARABIC => "المراجع", @@ -300,7 +296,7 @@ impl LocalName for BibliographyElem { #[ty] #[derive(Debug, Clone, PartialEq)] pub struct Bibliography { - map: Arc<IndexMap<EcoString, hayagriva::Entry>>, + map: Arc<IndexMap<PicoStr, hayagriva::Entry>>, hash: u128, } @@ -371,8 +367,8 @@ impl Bibliography { }) } - fn has(&self, key: &str) -> bool { - self.map.contains_key(key) + fn has(&self, key: impl Into<PicoStr>) -> bool { + self.map.contains_key(&key.into()) } fn entries(&self) -> impl Iterator<Item = &hayagriva::Entry> { @@ -645,7 +641,7 @@ impl<'a> Generator<'a> { let mut driver = BibliographyDriver::new(); for elem in &self.groups { let group = elem.to::<CiteGroup>().unwrap(); - let location = group.0.location().unwrap(); + let location = group.location().unwrap(); let children = group.children(); // Groups should never be empty. @@ -657,12 +653,13 @@ impl<'a> Generator<'a> { let mut normal = true; // Create infos and items for each child in the group. - for child in &children { - let key = child.key(); - let Some(entry) = database.map.get(&key.0) else { + for child in children { + let key = *child.key(); + let Some(entry) = database.map.get(&key.into_inner()) else { errors.push(error!( child.span(), - "key `{}` does not exist in the bibliography", key.0 + "key `{}` does not exist in the bibliography", + key.as_str() )); continue; }; @@ -714,13 +711,13 @@ impl<'a> Generator<'a> { driver.citation(CitationRequest::new( items, style, - Some(locale(first.lang(), first.region())), + Some(locale(*first.lang(), *first.region())), &LOCALES, None, )); } - let locale = locale(self.bibliography.lang(), self.bibliography.region()); + let locale = locale(*self.bibliography.lang(), *self.bibliography.region()); // Add hidden items for everything if we should print the whole // bibliography. @@ -761,7 +758,7 @@ impl<'a> Generator<'a> { // so that we can link there. let mut links = HashMap::new(); if let Some(bibliography) = &rendered.bibliography { - let location = self.bibliography.0.location().unwrap(); + let location = self.bibliography.location().unwrap(); for (k, item) in bibliography.items.iter().enumerate() { links.insert(item.key.as_str(), location.variant(k + 1)); } @@ -770,8 +767,7 @@ impl<'a> Generator<'a> { let mut output = std::mem::take(&mut self.failures); for (info, citation) in self.infos.iter().zip(&rendered.citations) { let supplement = |i: usize| info.subinfos.get(i)?.supplement.clone(); - let link = - |i: usize| links.get(info.subinfos.get(i)?.key.0.as_str()).copied(); + let link = |i: usize| links.get(info.subinfos.get(i)?.key.as_str()).copied(); let renderer = ElemRenderer { world: self.world, @@ -811,13 +807,13 @@ impl<'a> Generator<'a> { let mut first_occurances = HashMap::new(); for info in &self.infos { for subinfo in &info.subinfos { - let key = subinfo.key.0.as_str(); + let key = subinfo.key.as_str(); first_occurances.entry(key).or_insert(info.location); } } // The location of the bibliography. - let location = self.bibliography.0.location().unwrap(); + let location = self.bibliography.location().unwrap(); let mut output = vec![]; for (k, item) in rendered.items.iter().enumerate() { @@ -918,8 +914,8 @@ impl ElemRenderer<'_> { if let Some(prefix) = suf_prefix { const COLUMN_GUTTER: Em = Em::new(0.65); content = GridElem::new(vec![prefix, content]) - .with_columns(TrackSizings(vec![Sizing::Auto; 2])) - .with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()])) + .with_columns(TrackSizings(smallvec![Sizing::Auto; 2])) + .with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()])) .pack(); } diff --git a/crates/typst-library/src/meta/cite.rs b/crates/typst-library/src/meta/cite.rs index 2e3c4155..a61523d8 100644 --- a/crates/typst-library/src/meta/cite.rs +++ b/crates/typst-library/src/meta/cite.rs @@ -141,7 +141,7 @@ impl Show for CiteGroup { #[tracing::instrument(name = "CiteGroup::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { Ok(vt.delayed(|vt| { - let location = self.0.location().unwrap(); + let location = self.location().unwrap(); let span = self.span(); Works::generate(vt.world, vt.introspector) .at(span)? diff --git a/crates/typst-library/src/meta/context.rs b/crates/typst-library/src/meta/context.rs index 3a82a925..59b35577 100644 --- a/crates/typst-library/src/meta/context.rs +++ b/crates/typst-library/src/meta/context.rs @@ -37,7 +37,7 @@ impl Show for LocateElem { #[tracing::instrument(name = "LocateElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { Ok(vt.delayed(|vt| { - let location = self.0.location().unwrap(); + let location = self.location().unwrap(); Ok(self.func().call_vt(vt, [location])?.display()) })) } diff --git a/crates/typst-library/src/meta/counter.rs b/crates/typst-library/src/meta/counter.rs index 1ad0bff1..45041a38 100644 --- a/crates/typst-library/src/meta/counter.rs +++ b/crates/typst-library/src/meta/counter.rs @@ -284,9 +284,9 @@ impl Counter { } if let Some(update) = match elem.to::<UpdateElem>() { - Some(elem) => Some(elem.update()), + Some(elem) => Some(elem.update().clone()), None => match elem.with::<dyn Count>() { - Some(countable) => countable.update(), + Some(countable) => countable.update().clone(), None => Some(CounterUpdate::Step(NonZeroUsize::ONE)), }, } { @@ -301,8 +301,7 @@ impl Counter { /// The selector relevant for this counter's updates. fn selector(&self) -> Selector { - let mut selector = - Selector::Elem(UpdateElem::elem(), Some(dict! { "key" => self.0.clone() })); + let mut selector = select_where!(UpdateElem, Key => self.0.clone()); if let CounterKey::Selector(key) = &self.0 { selector = Selector::Or(eco_vec![selector, key.clone()]); @@ -613,17 +612,18 @@ impl Show for DisplayElem { #[tracing::instrument(name = "DisplayElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { Ok(vt.delayed(|vt| { - let location = self.0.location().unwrap(); + let location = self.location().unwrap(); let counter = self.counter(); let numbering = self .numbering() + .clone() .or_else(|| { let CounterKey::Selector(Selector::Elem(func, _)) = counter.0 else { return None; }; if func == HeadingElem::elem() { - HeadingElem::numbering_in(styles) + HeadingElem::numbering_in(styles).clone() } else if func == FigureElem::elem() { FigureElem::numbering_in(styles) } else if func == EquationElem::elem() { @@ -634,7 +634,7 @@ impl Show for DisplayElem { }) .unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into()); - let state = if self.both() { + let state = if *self.both() { counter.both(vt, location)? } else { counter.at(vt, location)? @@ -695,9 +695,9 @@ impl ManualPageCounter { FrameItem::Group(group) => self.visit(vt, &group.frame)?, FrameItem::Meta(Meta::Elem(elem), _) => { let Some(elem) = elem.to::<UpdateElem>() else { continue }; - if elem.key() == CounterKey::Page { + if *elem.key() == CounterKey::Page { let mut state = CounterState(smallvec![self.logical]); - state.update(vt, elem.update())?; + state.update(vt, elem.update().clone())?; self.logical = state.first(); } } diff --git a/crates/typst-library/src/meta/document.rs b/crates/typst-library/src/meta/document.rs index 55c365ac..f6ff6812 100644 --- a/crates/typst-library/src/meta/document.rs +++ b/crates/typst-library/src/meta/document.rs @@ -1,3 +1,4 @@ +use comemo::Prehashed; use typst::eval::Datetime; use crate::layout::{LayoutRoot, PageElem}; @@ -43,9 +44,8 @@ pub struct DocumentElem { pub date: Smart<Option<Datetime>>, /// The page runs. - #[internal] #[variadic] - pub children: Vec<Content>, + pub children: Vec<Prehashed<Content>>, } impl Construct for DocumentElem { @@ -64,7 +64,7 @@ impl LayoutRoot for DocumentElem { let mut page_counter = ManualPageCounter::new(); let children = self.children(); - let mut iter = children.iter().peekable(); + let mut iter = children.iter().map(|c| &**c).peekable(); while let Some(mut child) = iter.next() { let outer = styles; @@ -99,7 +99,7 @@ impl LayoutRoot for DocumentElem { } /// A list of authors. -#[derive(Debug, Default, Clone, Hash)] +#[derive(Debug, Default, Clone, PartialEq, Hash)] pub struct Author(Vec<EcoString>); cast! { @@ -110,7 +110,7 @@ cast! { } /// A list of keywords. -#[derive(Debug, Default, Clone, Hash)] +#[derive(Debug, Default, Clone, PartialEq, Hash)] pub struct Keywords(Vec<EcoString>); cast! { diff --git a/crates/typst-library/src/meta/figure.rs b/crates/typst-library/src/meta/figure.rs index 990c9860..bfcc9b44 100644 --- a/crates/typst-library/src/meta/figure.rs +++ b/crates/typst-library/src/meta/figure.rs @@ -1,9 +1,8 @@ +use std::borrow::Cow; use std::str::FromStr; use typst::util::option_eq; -use super::{ - Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern, -}; +use super::{Count, Counter, CounterKey, CounterUpdate, Numbering, NumberingPattern}; use crate::layout::{BlockElem, PlaceElem, VElem}; use crate::meta::{Outlinable, Refable, Supplement}; use crate::prelude::*; @@ -170,6 +169,7 @@ pub struct FigureElem { /// kind: "foo", /// ) /// ``` + #[borrowed] pub supplement: Smart<Option<Supplement>>, /// How to number the figure. Accepts a @@ -212,25 +212,21 @@ impl Synthesize for FigureElem { let kind = self.kind(styles).unwrap_or_else(|| { self.body() .query_first(Selector::can::<dyn Figurable>()) - .cloned() .map(|elem| FigureKind::Elem(elem.func())) .unwrap_or_else(|| FigureKind::Elem(ImageElem::elem())) }); // Resolve the supplement. - let supplement = match self.supplement(styles) { + let supplement = match self.supplement(styles).as_ref() { Smart::Auto => { // Default to the local name for the kind, if available. let name = match &kind { - FigureKind::Elem(func) => { - let empty = Content::new(*func); - empty.with::<dyn LocalName>().map(|c| { - TextElem::packed(c.local_name( - TextElem::lang_in(styles), - TextElem::region_in(styles), - )) - }) - } + FigureKind::Elem(func) => func + .local_name( + TextElem::lang_in(styles), + TextElem::region_in(styles), + ) + .map(TextElem::packed), FigureKind::Name(_) => None, }; @@ -245,24 +241,21 @@ impl Synthesize for FigureElem { // Resolve the supplement with the first descendant of the kind or // just the body, if none was found. let descendant = match kind { - FigureKind::Elem(func) => { - self.body().query_first(Selector::Elem(func, None)).cloned() - } + FigureKind::Elem(func) => self + .body() + .query_first(Selector::Elem(func, None)) + .map(Cow::Owned), FigureKind::Name(_) => None, }; - let target = descendant.unwrap_or_else(|| self.body()); + let target = descendant.unwrap_or_else(|| Cow::Borrowed(self.body())); Some(supplement.resolve(vt, [target])?) } }; // Construct the figure's counter. - let counter = Counter::new(CounterKey::Selector(Selector::Elem( - Self::elem(), - Some(dict! { - "kind" => kind.clone(), - }), - ))); + let counter = + Counter::new(CounterKey::Selector(select_where!(Self, Kind => kind.clone()))); // Fill the figure's caption. let mut caption = self.caption(styles); @@ -271,7 +264,7 @@ impl Synthesize for FigureElem { caption.push_supplement(supplement.clone()); caption.push_numbering(numbering.clone()); caption.push_counter(Some(counter.clone())); - caption.push_location(self.0.location()); + caption.push_figure_location(self.location()); } self.push_placement(self.placement(styles)); @@ -289,7 +282,7 @@ impl Synthesize for FigureElem { impl Show for FigureElem { #[tracing::instrument(name = "FigureElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let mut realized = self.body(); + let mut realized = self.body().clone(); // Build the caption, if any. if let Some(caption) = self.caption(styles) { @@ -339,14 +332,15 @@ impl Count for FigureElem { impl Refable for FigureElem { fn supplement(&self) -> Content { // After synthesis, this should always be custom content. - match self.supplement(StyleChain::default()) { - Smart::Custom(Some(Supplement::Content(content))) => content, + let default = StyleChain::default(); + match self.supplement(default).as_ref() { + Smart::Custom(Some(Supplement::Content(content))) => content.clone(), _ => Content::empty(), } } fn counter(&self) -> Counter { - self.counter().unwrap_or_else(|| Counter::of(Self::elem())) + self.counter().clone().unwrap_or_else(|| Counter::of(Self::elem())) } fn numbering(&self) -> Option<Numbering> { @@ -364,17 +358,17 @@ impl Outlinable for FigureElem { return Ok(None); }; - let mut realized = caption.body(); + let mut realized = caption.body().clone(); if let ( Smart::Custom(Some(Supplement::Content(mut supplement))), Some(counter), Some(numbering), ) = ( - self.supplement(StyleChain::default()), + self.supplement(StyleChain::default()).clone(), self.counter(), self.numbering(StyleChain::default()), ) { - let location = self.0.location().unwrap(); + let location = self.location().unwrap(); let numbers = counter.at(vt, location)?.display(vt, &numbering)?; if !supplement.is_empty() { @@ -497,8 +491,9 @@ pub struct FigureCaption { pub counter: Option<Counter>, /// The figure's location. + #[internal] #[synthesized] - pub location: Option<Location>, + pub figure_location: Option<Location>, } impl FigureCaption { @@ -542,12 +537,15 @@ impl Synthesize for FigureCaption { impl Show for FigureCaption { #[tracing::instrument(name = "FigureCaption::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let mut realized = self.body(); + let mut realized = self.body().clone(); - if let (Some(mut supplement), Some(numbering), Some(counter), Some(location)) = - (self.supplement(), self.numbering(), self.counter(), self.location()) - { - let numbers = counter.at(vt, location)?.display(vt, &numbering)?; + if let (Some(mut supplement), Some(numbering), Some(counter), Some(location)) = ( + self.supplement().clone(), + self.numbering(), + self.counter(), + self.figure_location(), + ) { + let numbers = counter.at(vt, *location)?.display(vt, numbering)?; if !supplement.is_empty() { supplement += TextElem::packed('\u{a0}'); } @@ -564,7 +562,7 @@ cast! { } /// The `kind` parameter of a [`FigureElem`]. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum FigureKind { /// The kind is an element function. Elem(Element), @@ -585,4 +583,4 @@ cast! { /// An element that can be auto-detected in a figure. /// /// This trait is used to determine the type of a figure. -pub trait Figurable: LocalName {} +pub trait Figurable {} diff --git a/crates/typst-library/src/meta/footnote.rs b/crates/typst-library/src/meta/footnote.rs index a8f0b4dd..4306f833 100644 --- a/crates/typst-library/src/meta/footnote.rs +++ b/crates/typst-library/src/meta/footnote.rs @@ -59,6 +59,7 @@ pub struct FootnoteElem { /// #footnote[Star], /// #footnote[Dagger] /// ``` + #[borrowed] #[default(Numbering::Pattern(NumberingPattern::from_str("1").unwrap()))] pub numbering: Numbering, @@ -91,7 +92,7 @@ impl FootnoteElem { } /// Returns the content of the body of this footnote if it is not a ref. - pub fn body_content(&self) -> Option<Content> { + pub fn body_content(&self) -> Option<&Content> { match self.body() { FootnoteBody::Content(content) => Some(content), _ => None, @@ -102,20 +103,20 @@ impl FootnoteElem { pub fn declaration_location(&self, vt: &Vt) -> StrResult<Location> { match self.body() { FootnoteBody::Reference(label) => { - let element: Prehashed<Content> = vt.introspector.query_label(&label)?; + let element: Prehashed<Content> = vt.introspector.query_label(*label)?; let footnote = element .to::<FootnoteElem>() .ok_or("referenced element should be a footnote")?; footnote.declaration_location(vt) } - _ => Ok(self.0.location().unwrap()), + _ => Ok(self.location().unwrap()), } } } impl Synthesize for FootnoteElem { fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> { - self.push_numbering(self.numbering(styles)); + self.push_numbering(self.numbering(styles).clone()); Ok(()) } } @@ -127,7 +128,7 @@ impl Show for FootnoteElem { let loc = self.declaration_location(vt).at(self.span())?; let numbering = self.numbering(styles); let counter = Counter::of(Self::elem()); - let num = counter.at(vt, loc)?.display(vt, &numbering)?; + let num = counter.at(vt, loc)?.display(vt, numbering)?; let sup = SuperElem::new(num).pack(); let loc = loc.variant(1); // Add zero-width weak spacing to make the footnote "sticky". @@ -144,7 +145,7 @@ impl Count for FootnoteElem { /// The body of a footnote can be either some content or a label referencing /// another footnote. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum FootnoteBody { Content(Content), Reference(Label), @@ -266,10 +267,11 @@ impl Show for FootnoteEntry { fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { let note = self.note(); let number_gap = Em::new(0.05); - let numbering = note.numbering(StyleChain::default()); + let default = StyleChain::default(); + let numbering = note.numbering(default); let counter = Counter::of(FootnoteElem::elem()); - let loc = note.0.location().unwrap(); - let num = counter.at(vt, loc)?.display(vt, &numbering)?; + let loc = note.location().unwrap(); + let num = counter.at(vt, loc)?.display(vt, numbering)?; let sup = SuperElem::new(num) .pack() .linked(Destination::Location(loc)) @@ -278,7 +280,7 @@ impl Show for FootnoteEntry { HElem::new(self.indent(styles).into()).pack(), sup, HElem::new(number_gap.into()).with_weak(true).pack(), - note.body_content().unwrap(), + note.body_content().unwrap().clone(), ])) } } diff --git a/crates/typst-library/src/meta/heading.rs b/crates/typst-library/src/meta/heading.rs index 34af8238..1a2f4dd0 100644 --- a/crates/typst-library/src/meta/heading.rs +++ b/crates/typst-library/src/meta/heading.rs @@ -3,7 +3,7 @@ use typst::util::option_eq; use super::{Counter, CounterUpdate, LocalName, Numbering, Outlinable, Refable}; use crate::layout::{BlockElem, HElem, VElem}; -use crate::meta::{Count, Supplement}; +use crate::meta::{Count, LocalNameIn, Supplement}; use crate::prelude::*; use crate::text::{SpaceElem, TextElem, TextSize}; @@ -54,6 +54,7 @@ pub struct HeadingElem { /// == A subsection /// === A sub-subsection /// ``` + #[borrowed] pub numbering: Option<Numbering>, /// A supplement for the heading. @@ -124,13 +125,13 @@ impl Synthesize for HeadingElem { fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> { // Resolve the supplement. let supplement = match self.supplement(styles) { - Smart::Auto => TextElem::packed(self.local_name_in(styles)), + Smart::Auto => TextElem::packed(Self::local_name_in(styles)), Smart::Custom(None) => Content::empty(), Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?, }; self.push_level(self.level(styles)); - self.push_numbering(self.numbering(styles)); + self.push_numbering(self.numbering(styles).clone()); self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement)))); self.push_outlined(self.outlined(styles)); self.push_bookmarked(self.bookmarked(styles)); @@ -142,10 +143,10 @@ impl Synthesize for HeadingElem { impl Show for HeadingElem { #[tracing::instrument(name = "HeadingElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let mut realized = self.body(); - if let Some(numbering) = self.numbering(styles) { + let mut realized = self.body().clone(); + if let Some(numbering) = self.numbering(styles).as_ref() { realized = Counter::of(Self::elem()) - .display(Some(numbering), false) + .display(Some(numbering.clone()), false) .spanned(self.span()) + HElem::new(Em::new(0.3).into()).with_weak(true).pack() + realized; @@ -204,7 +205,7 @@ impl Refable for HeadingElem { } fn numbering(&self) -> Option<Numbering> { - self.numbering(StyleChain::default()) + self.numbering(StyleChain::default()).clone() } } @@ -214,11 +215,12 @@ impl Outlinable for HeadingElem { return Ok(None); } - let mut content = self.body(); - if let Some(numbering) = self.numbering(StyleChain::default()) { + let mut content = self.body().clone(); + let default = StyleChain::default(); + if let Some(numbering) = self.numbering(default).as_ref() { let numbers = Counter::of(Self::elem()) - .at(vt, self.0.location().unwrap())? - .display(vt, &numbering)?; + .at(vt, self.location().unwrap())? + .display(vt, numbering)?; content = numbers + SpaceElem::new().pack() + content; }; @@ -231,7 +233,7 @@ impl Outlinable for HeadingElem { } impl LocalName for HeadingElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Kapitull", Lang::ARABIC => "الفصل", diff --git a/crates/typst-library/src/meta/link.rs b/crates/typst-library/src/meta/link.rs index 7b68b186..74857f1b 100644 --- a/crates/typst-library/src/meta/link.rs +++ b/crates/typst-library/src/meta/link.rs @@ -84,12 +84,12 @@ impl LinkElem { impl Show for LinkElem { #[tracing::instrument(name = "LinkElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { - let body = self.body(); + let body = self.body().clone(); let linked = match self.dest() { - LinkTarget::Dest(dest) => body.linked(dest), + LinkTarget::Dest(dest) => body.linked(dest.clone()), LinkTarget::Label(label) => vt .delayed(|vt| { - let elem = vt.introspector.query_label(&label).at(self.span())?; + let elem = vt.introspector.query_label(*label).at(self.span())?; let dest = Destination::Location(elem.location().unwrap()); Ok(Some(body.clone().linked(dest))) }) @@ -110,7 +110,7 @@ fn body_from_url(url: &EcoString) -> Content { } /// A target where a link can go. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum LinkTarget { Dest(Destination), Label(Label), diff --git a/crates/typst-library/src/meta/mod.rs b/crates/typst-library/src/meta/mod.rs index 1019864f..ffe861ab 100644 --- a/crates/typst-library/src/meta/mod.rs +++ b/crates/typst-library/src/meta/mod.rs @@ -62,13 +62,15 @@ pub(super) fn define(global: &mut Scope) { global.define_func::<query>(); } -/// The named with which an element is referenced. -pub trait LocalName { - /// Get the name in the given language and (optionally) region. - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str; - - /// Resolve the local name with a style chain. - fn local_name_in(&self, styles: StyleChain) -> &'static str { - self.local_name(TextElem::lang_in(styles), TextElem::region_in(styles)) +/// An element that has a local name. +pub trait LocalNameIn: LocalName { + /// Gets the local name from the style chain. + fn local_name_in(styles: StyleChain) -> &'static str + where + Self: Sized, + { + Self::local_name(TextElem::lang_in(styles), TextElem::region_in(styles)) } } + +impl<T: LocalName> LocalNameIn for T {} diff --git a/crates/typst-library/src/meta/outline.rs b/crates/typst-library/src/meta/outline.rs index 9266ba2b..e7332ab3 100644 --- a/crates/typst-library/src/meta/outline.rs +++ b/crates/typst-library/src/meta/outline.rs @@ -6,6 +6,7 @@ use super::{ Counter, CounterKey, HeadingElem, LocalName, Numbering, NumberingPattern, Refable, }; use crate::layout::{BoxElem, HElem, HideElem, ParbreakElem, RepeatElem, Spacing}; +use crate::meta::LocalNameIn; use crate::prelude::*; use crate::text::{LinebreakElem, SpaceElem, TextElem}; @@ -88,10 +89,8 @@ pub struct OutlineElem { /// caption: [Experiment results], /// ) /// ``` - #[default(LocatableSelector(Selector::Elem( - HeadingElem::elem(), - Some(dict! { "outlined" => true }) - )))] + #[default(LocatableSelector(select_where!(HeadingElem, Outlined => true)))] + #[borrowed] pub target: LocatableSelector, /// The maximum level up to which elements are included in the outline. When @@ -161,6 +160,7 @@ pub struct OutlineElem { /// #lorem(10) /// ``` #[default(None)] + #[borrowed] pub indent: Option<Smart<OutlineIndent>>, /// Content to fill the space between the title and the page number. Can be @@ -187,14 +187,9 @@ impl Show for OutlineElem { let mut seq = vec![ParbreakElem::new().pack()]; // Build the outline title. if let Some(title) = self.title(styles) { - let title = - title.unwrap_or_else(|| { - TextElem::packed(self.local_name( - TextElem::lang_in(styles), - TextElem::region_in(styles), - )) - .spanned(self.span()) - }); + let title = title.unwrap_or_else(|| { + TextElem::packed(Self::local_name_in(styles)).spanned(self.span()) + }); seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack()); } @@ -217,7 +212,7 @@ impl Show for OutlineElem { }; let level = entry.level(); - if depth < level { + if depth < *level { continue; } @@ -226,12 +221,12 @@ impl Show for OutlineElem { while ancestors .last() .and_then(|ancestor| ancestor.with::<dyn Outlinable>()) - .map_or(false, |last| last.level() >= level) + .map_or(false, |last| last.level() >= *level) { ancestors.pop(); } - OutlineIndent::apply(&indent, vt, &ancestors, &mut seq, self.span())?; + OutlineIndent::apply(indent, vt, &ancestors, &mut seq, self.span())?; // Add the overridable outline entry, followed by a line break. seq.push(entry.pack()); @@ -255,7 +250,7 @@ impl Finalize for OutlineElem { } impl LocalName for OutlineElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Përmbajtja", Lang::ARABIC => "المحتويات", @@ -301,7 +296,7 @@ pub trait Outlinable: Refable { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum OutlineIndent { Bool(bool), Rel(Rel<Length>), @@ -497,14 +492,14 @@ impl Show for OutlineEntry { }; // The body text remains overridable. - seq.push(self.body().linked(Destination::Location(location))); + seq.push(self.body().clone().linked(Destination::Location(location))); // Add filler symbols between the section name and page number. if let Some(filler) = self.fill() { seq.push(SpaceElem::new().pack()); seq.push( BoxElem::new() - .with_body(Some(filler)) + .with_body(Some(filler.clone())) .with_width(Fr::one().into()) .pack(), ); @@ -514,7 +509,7 @@ impl Show for OutlineEntry { } // Add the page number. - let page = self.page().linked(Destination::Location(location)); + let page = self.page().clone().linked(Destination::Location(location)); seq.push(page); Ok(Content::sequence(seq)) diff --git a/crates/typst-library/src/meta/reference.rs b/crates/typst-library/src/meta/reference.rs index 12e13929..3a463c80 100644 --- a/crates/typst-library/src/meta/reference.rs +++ b/crates/typst-library/src/meta/reference.rs @@ -115,6 +115,7 @@ pub struct RefElem { /// in @intro[Part], it is done /// manually. /// ``` + #[borrowed] pub supplement: Smart<Option<Supplement>>, /// A synthesized citation. @@ -132,9 +133,9 @@ impl Synthesize for RefElem { self.push_citation(Some(citation)); self.push_element(None); - let target = self.target(); - if !BibliographyElem::has(vt, &target.0) { - if let Ok(elem) = vt.introspector.query_label(&target) { + let target = *self.target(); + if !BibliographyElem::has(vt, target) { + if let Ok(elem) = vt.introspector.query_label(target) { self.push_element(Some(elem.into_inner())); return Ok(()); } @@ -148,22 +149,22 @@ impl Show for RefElem { #[tracing::instrument(name = "RefElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { Ok(vt.delayed(|vt| { - let target = self.target(); - let elem = vt.introspector.query_label(&self.target()); + let target = *self.target(); + let elem = vt.introspector.query_label(target); let span = self.span(); - if BibliographyElem::has(vt, &target.0) { + if BibliographyElem::has(vt, target) { if elem.is_ok() { bail!(span, "label occurs in the document and its bibliography"); } - return Ok(self.to_citation(vt, styles)?.pack().spanned(span)); + return Ok(self.to_citation(vt, styles)?.spanned(span).pack()); } let elem = elem.at(span)?; if elem.func() == FootnoteElem::elem() { - return Ok(FootnoteElem::with_label(target).pack().spanned(span)); + return Ok(FootnoteElem::with_label(target).spanned(span).pack()); } let refable = elem @@ -204,7 +205,7 @@ impl Show for RefElem { .at(vt, elem.location().unwrap())? .display(vt, &numbering.trimmed())?; - let supplement = match self.supplement(styles) { + let supplement = match self.supplement(styles).as_ref() { Smart::Auto => refable.supplement(), Smart::Custom(None) => Content::empty(), Smart::Custom(Some(supplement)) => { @@ -225,10 +226,10 @@ impl Show for RefElem { impl RefElem { /// Turn the reference into a citation. pub fn to_citation(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<CiteElem> { - let mut elem = CiteElem::new(self.target()); - elem.0.set_location(self.0.location().unwrap()); + let mut elem = CiteElem::new(*self.target()); + elem.set_location(self.location().unwrap()); elem.synthesize(vt, styles)?; - elem.push_supplement(match self.supplement(styles) { + elem.push_supplement(match self.supplement(styles).clone() { Smart::Custom(Some(Supplement::Content(content))) => Some(content), _ => None, }); @@ -238,6 +239,7 @@ impl RefElem { } /// Additional content for a reference. +#[derive(Debug, Clone, PartialEq, Hash)] pub enum Supplement { Content(Content), Func(Func), diff --git a/crates/typst-library/src/meta/state.rs b/crates/typst-library/src/meta/state.rs index 137b754e..4f04628f 100644 --- a/crates/typst-library/src/meta/state.rs +++ b/crates/typst-library/src/meta/state.rs @@ -233,7 +233,7 @@ impl State { for elem in introspector.query(&self.selector()) { let elem = elem.to::<UpdateElem>().unwrap(); match elem.update() { - StateUpdate::Set(value) => state = value, + StateUpdate::Set(value) => state = value.clone(), StateUpdate::Func(func) => state = func.call_vt(&mut vt, [state])?, } stops.push(state.clone()); @@ -244,7 +244,7 @@ impl State { /// The selector for this state's updates. fn selector(&self) -> Selector { - Selector::Elem(UpdateElem::elem(), Some(dict! { "key" => self.key.clone() })) + select_where!(UpdateElem, Key => self.key.clone()) } } @@ -383,7 +383,7 @@ impl Show for DisplayElem { #[tracing::instrument(name = "DisplayElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { Ok(vt.delayed(|vt| { - let location = self.0.location().unwrap(); + let location = self.location().unwrap(); let value = self.state().at(vt, location)?; Ok(match self.func() { Some(func) => func.call_vt(vt, [value])?.display(), diff --git a/crates/typst-library/src/prelude.rs b/crates/typst-library/src/prelude.rs index 329cbf61..e3163e0f 100644 --- a/crates/typst-library/src/prelude.rs +++ b/crates/typst-library/src/prelude.rs @@ -10,6 +10,8 @@ pub use comemo::{Track, Tracked, TrackedMut}; #[doc(no_inline)] pub use ecow::{eco_format, EcoString}; #[doc(no_inline)] +pub use smallvec::{smallvec, SmallVec}; +#[doc(no_inline)] pub use typst::diag::{bail, error, At, Hint, SourceResult, StrResult}; #[doc(no_inline)] pub use typst::doc::*; @@ -22,10 +24,10 @@ pub use typst::eval::{ pub use typst::geom::*; #[doc(no_inline)] pub use typst::model::{ - elem, Behave, Behaviour, Construct, Content, Element, Finalize, Fold, Introspector, - Label, Locatable, LocatableSelector, Location, Locator, MetaElem, NativeElement, - PlainText, Resolve, Selector, Set, Show, StyleChain, StyleVec, Styles, Synthesize, - Unlabellable, Vt, + elem, select_where, Behave, Behaviour, Construct, Content, Element, ElementFields, + Finalize, Fold, Introspector, Label, LocalName, Locatable, LocatableSelector, + Location, Locator, MetaElem, NativeElement, PlainText, Resolve, Selector, Set, Show, + StyleChain, StyleVec, Styles, Synthesize, Unlabellable, Vt, }; #[doc(no_inline)] pub use typst::syntax::{FileId, Span, Spanned}; diff --git a/crates/typst-library/src/shared/behave.rs b/crates/typst-library/src/shared/behave.rs index f97e3fbc..e152fbb5 100644 --- a/crates/typst-library/src/shared/behave.rs +++ b/crates/typst-library/src/shared/behave.rs @@ -1,16 +1,18 @@ //! Element interaction. +use std::borrow::Cow; + use typst::model::{Behave, Behaviour, Content, StyleChain, StyleVec, StyleVecBuilder}; /// A wrapper around a [`StyleVecBuilder`] that allows elements to interact. #[derive(Debug)] pub struct BehavedBuilder<'a> { /// The internal builder. - builder: StyleVecBuilder<'a, Content>, + builder: StyleVecBuilder<'a, Cow<'a, Content>>, /// Staged weak and ignorant elements that we can't yet commit to the /// builder. The option is `Some(_)` for weak elements and `None` for /// ignorant elements. - staged: Vec<(Content, Behaviour, StyleChain<'a>)>, + staged: Vec<(Cow<'a, Content>, Behaviour, StyleChain<'a>)>, /// What the last non-ignorant item was. last: Behaviour, } @@ -41,7 +43,7 @@ impl<'a> BehavedBuilder<'a> { } /// Push an item into the sequence. - pub fn push(&mut self, elem: Content, styles: StyleChain<'a>) { + pub fn push(&mut self, elem: Cow<'a, Content>, styles: StyleChain<'a>) { let interaction = elem .with::<dyn Behave>() .map_or(Behaviour::Supportive, Behave::behaviour); @@ -81,12 +83,12 @@ impl<'a> BehavedBuilder<'a> { } /// Iterate over the contained elements. - pub fn elems(&self) -> impl DoubleEndedIterator<Item = &Content> { + pub fn elems(&self) -> impl DoubleEndedIterator<Item = &Cow<'a, Content>> { self.builder.elems().chain(self.staged.iter().map(|(item, ..)| item)) } /// Return the finish style vec and the common prefix chain. - pub fn finish(mut self) -> (StyleVec<Content>, StyleChain<'a>) { + pub fn finish(mut self) -> (StyleVec<Cow<'a, Content>>, StyleChain<'a>) { self.flush(false); self.builder.finish() } diff --git a/crates/typst-library/src/shared/ext.rs b/crates/typst-library/src/shared/ext.rs index e6431423..60614820 100644 --- a/crates/typst-library/src/shared/ext.rs +++ b/crates/typst-library/src/shared/ext.rs @@ -47,13 +47,13 @@ impl ContentExt for Content { } fn linked(self, dest: Destination) -> Self { - self.styled(MetaElem::set_data(vec![Meta::Link(dest)])) + self.styled(MetaElem::set_data(smallvec![Meta::Link(dest)])) } fn backlinked(self, loc: Location) -> Self { let mut backlink = Content::empty(); backlink.set_location(loc); - self.styled(MetaElem::set_data(vec![Meta::Elem(backlink)])) + self.styled(MetaElem::set_data(smallvec![Meta::Elem(backlink)])) } fn aligned(self, align: Align) -> Self { @@ -85,7 +85,7 @@ impl StylesExt for Styles { fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) { self.set(TextElem::set_font(FontList( std::iter::once(preferred) - .chain(TextElem::font_in(existing)) + .chain(TextElem::font_in(existing).into_iter().cloned()) .collect(), ))); } diff --git a/crates/typst-library/src/text/deco.rs b/crates/typst-library/src/text/deco.rs index 136dfad8..d8167788 100644 --- a/crates/typst-library/src/text/deco.rs +++ b/crates/typst-library/src/text/deco.rs @@ -78,7 +78,7 @@ pub struct UnderlineElem { impl Show for UnderlineElem { #[tracing::instrument(name = "UnderlineElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_deco(Decoration { + Ok(self.body().clone().styled(TextElem::set_deco(Decoration { line: DecoLine::Underline { stroke: self.stroke(styles).unwrap_or_default(), offset: self.offset(styles), @@ -170,7 +170,7 @@ pub struct OverlineElem { impl Show for OverlineElem { #[tracing::instrument(name = "OverlineElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_deco(Decoration { + Ok(self.body().clone().styled(TextElem::set_deco(Decoration { line: DecoLine::Overline { stroke: self.stroke(styles).unwrap_or_default(), offset: self.offset(styles), @@ -247,7 +247,7 @@ pub struct StrikeElem { impl Show for StrikeElem { #[tracing::instrument(name = "StrikeElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_deco(Decoration { + Ok(self.body().clone().styled(TextElem::set_deco(Decoration { // Note that we do not support evade option for strikethrough. line: DecoLine::Strikethrough { stroke: self.stroke(styles).unwrap_or_default(), @@ -317,7 +317,7 @@ pub struct HighlightElem { impl Show for HighlightElem { #[tracing::instrument(name = "HighlightElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_deco(Decoration { + Ok(self.body().clone().styled(TextElem::set_deco(Decoration { line: DecoLine::Highlight { fill: self.fill(styles), top_edge: self.top_edge(styles), diff --git a/crates/typst-library/src/text/misc.rs b/crates/typst-library/src/text/misc.rs index 73657345..9f768f11 100644 --- a/crates/typst-library/src/text/misc.rs +++ b/crates/typst-library/src/text/misc.rs @@ -2,9 +2,15 @@ use super::TextElem; use crate::prelude::*; /// A text space. -#[elem(Behave, Unlabellable, PlainText)] +#[elem(Behave, Unlabellable, PlainText, Repr)] pub struct SpaceElem {} +impl Repr for SpaceElem { + fn repr(&self) -> EcoString { + EcoString::inline("[ ]") + } +} + impl Behave for SpaceElem { fn behaviour(&self) -> Behaviour { Behaviour::Weak(2) @@ -98,7 +104,10 @@ pub struct StrongElem { impl Show for StrongElem { #[tracing::instrument(name = "StrongElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_delta(Delta(self.delta(styles))))) + Ok(self + .body() + .clone() + .styled(TextElem::set_delta(Delta(self.delta(styles))))) } } @@ -153,7 +162,7 @@ pub struct EmphElem { impl Show for EmphElem { #[tracing::instrument(name = "EmphElem::show", skip(self))] fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> { - Ok(self.body().styled(TextElem::set_emph(Toggle))) + Ok(self.body().clone().styled(TextElem::set_emph(Toggle))) } } diff --git a/crates/typst-library/src/text/mod.rs b/crates/typst-library/src/text/mod.rs index 27dd4a3f..84a68d6f 100644 --- a/crates/typst-library/src/text/mod.rs +++ b/crates/typst-library/src/text/mod.rs @@ -64,7 +64,7 @@ pub(super) fn define(global: &mut Scope) { /// With a function call. /// ]) /// ``` -#[elem(Construct, PlainText)] +#[elem(Construct, PlainText, Repr)] pub struct TextElem { /// A font family name or priority list of font family names. /// @@ -97,6 +97,7 @@ pub struct TextElem { /// هذا عربي. /// ``` #[default(FontList(vec![FontFamily::new("Linux Libertine")]))] + #[borrowed] pub font: FontList, /// Whether to allow last resort font fallback when the primary font list @@ -558,8 +559,8 @@ pub struct TextElem { pub body: Content, /// The text. - #[internal] #[required] + #[variant(0)] pub text: EcoString, /// A delta to apply on the font weight. @@ -595,6 +596,12 @@ impl TextElem { } } +impl Repr for TextElem { + fn repr(&self) -> EcoString { + eco_format!("[{}]", self.text) + } +} + impl Construct for TextElem { fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> { // The text constructor is special: It doesn't create a text element. @@ -608,7 +615,7 @@ impl Construct for TextElem { impl PlainText for TextElem { fn plain_text(&self, text: &mut EcoString) { - text.push_str(&self.text()); + text.push_str(self.text()); } } @@ -644,12 +651,12 @@ cast! { #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct FontList(pub Vec<FontFamily>); -impl IntoIterator for FontList { - type IntoIter = std::vec::IntoIter<FontFamily>; - type Item = FontFamily; +impl<'a> IntoIterator for &'a FontList { + type IntoIter = std::slice::Iter<'a, FontFamily>; + type Item = &'a FontFamily; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.0.iter() } } diff --git a/crates/typst-library/src/text/quote.rs b/crates/typst-library/src/text/quote.rs index 86be2416..c555df2d 100644 --- a/crates/typst-library/src/text/quote.rs +++ b/crates/typst-library/src/text/quote.rs @@ -101,6 +101,7 @@ pub struct QuoteElem { /// /// #bibliography("works.bib") /// ``` + #[borrowed] attribution: Option<Attribution>, /// The quote. @@ -108,7 +109,7 @@ pub struct QuoteElem { body: Content, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Hash)] pub enum Attribution { Content(Content), Label(Label), @@ -126,7 +127,7 @@ cast! { impl Show for QuoteElem { fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let mut realized = self.body(); + let mut realized = self.body().clone(); let block = self.block(styles); if self.quotes(styles) == Smart::Custom(true) || !block { @@ -140,16 +141,16 @@ impl Show for QuoteElem { if block { realized = BlockElem::new().with_body(Some(realized)).pack(); - if let Some(attribution) = self.attribution(styles) { + if let Some(attribution) = self.attribution(styles).as_ref() { let mut seq = vec![TextElem::packed('—'), SpaceElem::new().pack()]; match attribution { Attribution::Content(content) => { - seq.push(content); + seq.push(content.clone()); } Attribution::Label(label) => { seq.push( - CiteElem::new(label) + CiteElem::new(*label) .with_form(Some(CitationForm::Prose)) .pack(), ); @@ -164,7 +165,7 @@ impl Show for QuoteElem { realized = PadElem::new(realized).pack(); } else if let Some(Attribution::Label(label)) = self.attribution(styles) { - realized += SpaceElem::new().pack() + CiteElem::new(label).pack(); + realized += SpaceElem::new().pack() + CiteElem::new(*label).pack(); } Ok(realized) diff --git a/crates/typst-library/src/text/quotes.rs b/crates/typst-library/src/text/quotes.rs index 37e664fd..035ba422 100644 --- a/crates/typst-library/src/text/quotes.rs +++ b/crates/typst-library/src/text/quotes.rs @@ -78,6 +78,7 @@ pub struct SmartquoteElem { /// #set smartquote(quotes: (single: ("[[", "]]"), double: auto)) /// 'Das sind eigene Anführungszeichen.' /// ``` + #[borrowed] pub quotes: Smart<QuoteDict>, } diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index d16659be..4f672be9 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -17,7 +17,7 @@ use super::{ FontFamily, FontList, Hyphenate, LinebreakElem, SmartquoteElem, TextElem, TextSize, }; use crate::layout::BlockElem; -use crate::meta::{Figurable, LocalName}; +use crate::meta::Figurable; use crate::prelude::*; // Shorthand for highlighter closures. @@ -145,6 +145,7 @@ pub struct RawElem { /// /// This is ```typ also *Typst*```, but inline! /// ```` + #[borrowed] pub lang: Option<EcoString>, /// The horizontal alignment that each line in a raw block should have. @@ -225,11 +226,13 @@ pub struct RawElem { let (theme_path, theme_data) = parse_theme(vm, args)?; theme_path.map(Some) )] + #[borrowed] pub theme: Option<EcoString>, /// The raw file buffer of syntax theme file. #[internal] #[parse(theme_data.map(Some))] + #[borrowed] pub theme_data: Option<Bytes>, /// The size for a tab stop in spaces. A tab is replaced with enough spaces to @@ -252,7 +255,7 @@ pub struct RawElem { /// Made accessible for the [`raw.line` element]($raw.line). /// Allows more styling control in `show` rules. #[synthesized] - pub lines: Vec<Content>, + pub lines: Vec<RawLine>, } #[scope] @@ -280,17 +283,20 @@ impl RawElem { impl Synthesize for RawElem { fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> { - self.push_lang(self.lang(styles)); + self.push_lang(self.lang(styles).clone()); - let mut text = self.text(); + let mut text = self.text().clone(); if text.contains('\t') { let tab_size = RawElem::tab_size_in(styles); text = align_tabs(&text, tab_size); } + let count = text.lines().count() as i64; + let lang = self .lang(styles) .as_ref() + .as_ref() .map(|s| s.to_lowercase()) .or(Some("txt".into())); @@ -298,12 +304,12 @@ impl Synthesize for RawElem { load_syntaxes(&self.syntaxes(styles), &self.syntaxes_data(styles)).unwrap() }); - let theme = self.theme(styles).map(|theme_path| { - load_theme(theme_path, self.theme_data(styles).unwrap()).unwrap() + let theme = self.theme(styles).as_ref().as_ref().map(|theme_path| { + load_theme(theme_path, self.theme_data(styles).as_ref().as_ref().unwrap()) + .unwrap() }); let theme = theme.as_deref().unwrap_or(&THEME); - let foreground = theme.settings.foreground.unwrap_or(synt::Color::BLACK); let mut seq = vec![]; @@ -319,15 +325,12 @@ impl Synthesize for RawElem { synt::Highlighter::new(theme), &mut |_, range, style| styled(&text[range], foreground, style), &mut |i, range, line| { - seq.push( - RawLine::new( - i + 1, - text.split(is_newline).count() as i64, - EcoString::from(&text[range]), - Content::sequence(line.drain(..)), - ) - .pack(), - ); + seq.push(RawLine::new( + i + 1, + count, + EcoString::from(&text[range]), + Content::sequence(line.drain(..)), + )); }, ) .highlight(); @@ -342,7 +345,6 @@ impl Synthesize for RawElem { }) }) { let mut highlighter = syntect::easy::HighlightLines::new(syntax, theme); - let len = text.lines().count(); for (i, line) in text.lines().enumerate() { let mut line_content = vec![]; for (style, piece) in @@ -351,18 +353,23 @@ impl Synthesize for RawElem { line_content.push(styled(piece, foreground, style)); } - seq.push( - RawLine::new( - i as i64 + 1, - len as i64, - EcoString::from(line), - Content::sequence(line_content), - ) - .pack(), - ); + seq.push(RawLine::new( + i as i64 + 1, + count, + EcoString::from(line), + Content::sequence(line_content), + )); } } else { - seq.extend(text.lines().map(TextElem::packed)); + let lines = text.lines(); + seq.extend(lines.enumerate().map(|(i, line)| { + RawLine::new( + i as i64 + 1, + count, + EcoString::from(line), + TextElem::packed(line), + ) + })); }; self.push_lines(seq); @@ -375,12 +382,12 @@ impl Show for RawElem { #[tracing::instrument(name = "RawElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { let mut lines = EcoVec::with_capacity((2 * self.lines().len()).saturating_sub(1)); - for (i, line) in self.lines().into_iter().enumerate() { + for (i, line) in self.lines().iter().enumerate() { if i != 0 { lines.push(LinebreakElem::new().pack()); } - lines.push(line); + lines.push(line.clone().pack()); } let mut realized = Content::sequence(lines); @@ -408,7 +415,7 @@ impl Finalize for RawElem { } impl LocalName for RawElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "List", Lang::ARABIC => "قائمة", @@ -443,7 +450,7 @@ impl Figurable for RawElem {} impl PlainText for RawElem { fn plain_text(&self, text: &mut EcoString) { - text.push_str(&self.text()); + text.push_str(self.text()); } } @@ -475,13 +482,13 @@ pub struct RawLine { impl Show for RawLine { fn show(&self, _vt: &mut Vt, _styles: StyleChain) -> SourceResult<Content> { - Ok(self.body()) + Ok(self.body().clone()) } } impl PlainText for RawLine { fn plain_text(&self, text: &mut EcoString) { - text.push_str(&self.text()); + text.push_str(self.text()); } } @@ -617,7 +624,7 @@ fn to_syn(color: Color) -> synt::Color { } /// A list of bibliography file paths. -#[derive(Debug, Default, Clone, Hash)] +#[derive(Debug, Default, Clone, PartialEq, Hash)] pub struct SyntaxPaths(Vec<EcoString>); cast! { @@ -681,7 +688,7 @@ fn parse_syntaxes( } #[comemo::memoize] -fn load_theme(path: EcoString, bytes: Bytes) -> StrResult<Arc<synt::Theme>> { +fn load_theme(path: &str, bytes: &Bytes) -> StrResult<Arc<synt::Theme>> { let mut cursor = std::io::Cursor::new(bytes.as_slice()); synt::ThemeSet::load_from_reader(&mut cursor) @@ -705,7 +712,7 @@ fn parse_theme( let data = vm.world().file(id).at(span)?; // Check that parsing works. - let _ = load_theme(path.clone(), data.clone()).at(span)?; + let _ = load_theme(&path, &data).at(span)?; Ok((Some(path), Some(data))) } diff --git a/crates/typst-library/src/text/shaping.rs b/crates/typst-library/src/text/shaping.rs index 0cfffce0..f7cf90bd 100644 --- a/crates/typst-library/src/text/shaping.rs +++ b/crates/typst-library/src/text/shaping.rs @@ -8,7 +8,7 @@ use typst::font::{Font, FontStyle, FontVariant}; use typst::util::SliceExt; use unicode_script::{Script, UnicodeScript}; -use super::{decorate, FontFamily, NumberType, NumberWidth, TextElem}; +use super::{decorate, NumberType, NumberWidth, TextElem}; use crate::layout::SpanMapper; use crate::prelude::*; @@ -320,7 +320,7 @@ impl<'a> ShapedText<'a> { for family in families(self.styles) { if let Some(font) = world .book() - .select(family.as_str(), self.variant) + .select(family, self.variant) .and_then(|id| world.font(id)) { expand(&font, None); @@ -424,7 +424,7 @@ impl<'a> ShapedText<'a> { None }; let mut chain = families(self.styles) - .map(|family| book.select(family.as_str(), self.variant)) + .map(|family| book.select(family, self.variant)) .chain(fallback_func.iter().map(|f| f())) .flatten(); @@ -593,11 +593,11 @@ pub fn shape<'a>( } /// Shape text with font fallback using the `families` iterator. -fn shape_segment( +fn shape_segment<'a>( ctx: &mut ShapingContext, base: usize, text: &str, - mut families: impl Iterator<Item = FontFamily> + Clone, + mut families: impl Iterator<Item = &'a str> + Clone, ) { // Fonts dont have newlines and tabs. if text.chars().all(|c| c == '\n' || c == '\t') { @@ -608,7 +608,7 @@ fn shape_segment( let world = ctx.vt.world; let book = world.book(); let mut selection = families.find_map(|family| { - book.select(family.as_str(), ctx.variant) + book.select(family, ctx.variant) .and_then(|id| world.font(id)) .filter(|font| !ctx.used.contains(font)) }); @@ -871,7 +871,7 @@ pub fn variant(styles: StyleChain) -> FontVariant { } /// Resolve a prioritized iterator over the font families. -pub fn families(styles: StyleChain) -> impl Iterator<Item = FontFamily> + Clone { +pub fn families(styles: StyleChain) -> impl Iterator<Item = &str> + Clone { const FALLBACKS: &[&str] = &[ "linux libertine", "twitter color emoji", @@ -883,7 +883,8 @@ pub fn families(styles: StyleChain) -> impl Iterator<Item = FontFamily> + Clone let tail = if TextElem::fallback_in(styles) { FALLBACKS } else { &[] }; TextElem::font_in(styles) .into_iter() - .chain(tail.iter().copied().map(FontFamily::new)) + .map(|family| family.as_str()) + .chain(tail.iter().copied()) } /// Collect the tags of the OpenType features to apply. diff --git a/crates/typst-library/src/text/shift.rs b/crates/typst-library/src/text/shift.rs index 6cb4d895..903982ef 100644 --- a/crates/typst-library/src/text/shift.rs +++ b/crates/typst-library/src/text/shift.rs @@ -44,7 +44,7 @@ pub struct SubElem { impl Show for SubElem { #[tracing::instrument(name = "SubElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let body = self.body(); + let body = self.body().clone(); let mut transformed = None; if self.typographic(styles) { if let Some(text) = search_text(&body, true) { @@ -104,7 +104,7 @@ pub struct SuperElem { impl Show for SuperElem { #[tracing::instrument(name = "SuperElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - let body = self.body(); + let body = self.body().clone(); let mut transformed = None; if self.typographic(styles) { if let Some(text) = search_text(&body, false) { @@ -127,7 +127,7 @@ fn search_text(content: &Content, sub: bool) -> Option<EcoString> { if content.is::<SpaceElem>() { Some(' '.into()) } else if let Some(elem) = content.to::<TextElem>() { - convert_script(&elem.text(), sub) + convert_script(elem.text(), sub) } else if let Some(children) = content.to_sequence() { let mut full = EcoString::new(); for item in children { diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs index 931e38c6..6e166bc0 100644 --- a/crates/typst-library/src/visualize/image.rs +++ b/crates/typst-library/src/visualize/image.rs @@ -6,7 +6,7 @@ use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat}; use typst::util::option_eq; use crate::compute::Readable; -use crate::meta::{Figurable, LocalName}; +use crate::meta::Figurable; use crate::prelude::*; use crate::text::families; @@ -41,6 +41,7 @@ pub struct ImageElem { let data = vm.world().file(id).at(span)?; path )] + #[borrowed] pub path: EcoString, /// The raw file data. @@ -157,11 +158,11 @@ impl Layout for ImageElem { }; let image = Image::with_fonts( - data.into(), + data.clone().into(), format, self.alt(styles), vt.world, - &families(styles).map(|s| s.as_str().into()).collect::<Vec<_>>(), + &families(styles).map(|s| s.into()).collect::<Vec<_>>(), ) .at(self.span())?; @@ -223,7 +224,7 @@ impl Layout for ImageElem { } impl LocalName for ImageElem { - fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str { + fn local_name(lang: Lang, region: Option<Region>) -> &'static str { match lang { Lang::ALBANIAN => "Figurë", Lang::ARABIC => "شكل", diff --git a/crates/typst-library/src/visualize/path.rs b/crates/typst-library/src/visualize/path.rs index 79364b07..43b11526 100644 --- a/crates/typst-library/src/visualize/path.rs +++ b/crates/typst-library/src/visualize/path.rs @@ -77,7 +77,7 @@ impl Layout for PathElem { .to_point() }; - let vertices: Vec<PathVertex> = self.vertices(); + let vertices = self.vertices(); let points: Vec<Point> = vertices.iter().map(|c| resolve(c.vertex())).collect(); let mut size = Size::zero(); |
