diff --git a/library/src/math/accent.rs b/library/src/math/accent.rs index 03caf4113..9ef762793 100644 --- a/library/src/math/accent.rs +++ b/library/src/math/accent.rs @@ -66,7 +66,7 @@ impl LayoutMath for AccentNode { // 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); + 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; diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs index 991d582c6..bd44546d1 100644 --- a/library/src/math/ctx.rs +++ b/library/src/math/ctx.rs @@ -124,13 +124,15 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { .into_frame()) } - pub fn layout_text(&mut self, text: &str) -> SourceResult<()> { + pub fn layout_text(&mut self, node: &TextNode) -> SourceResult<()> { + let text = node.text(); + let span = node.span(); let mut chars = text.chars(); if let Some(glyph) = chars .next() .filter(|_| chars.next().is_none()) .map(|c| self.style.styled_char(c)) - .and_then(|c| GlyphFragment::try_new(self, c)) + .and_then(|c| GlyphFragment::try_new(self, c, span)) { // A single letter that is available in the math font. if self.style.size == MathSize::Display @@ -146,7 +148,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { let mut fragments = vec![]; for c in text.chars() { let c = self.style.styled_char(c); - fragments.push(GlyphFragment::new(self, c).into()); + fragments.push(GlyphFragment::new(self, c, span).into()); } let frame = MathRow::new(fragments).to_frame(self); self.push(FrameFragment::new(self, frame)); @@ -158,7 +160,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { style = style.with_italic(false); } let text: EcoString = text.chars().map(|c| style.styled_char(c)).collect(); - let frame = self.layout_content(&TextNode::packed(text))?; + let frame = self.layout_content(&TextNode::packed(text).spanned(span))?; self.push( FrameFragment::new(self, frame) .with_class(MathClass::Alphabetic) diff --git a/library/src/math/delimited.rs b/library/src/math/delimited.rs index 6f468af7c..2b9ee5ed2 100644 --- a/library/src/math/delimited.rs +++ b/library/src/math/delimited.rs @@ -89,7 +89,9 @@ fn scale( ) { let glyph = match fragment { MathFragment::Glyph(glyph) => glyph.clone(), - MathFragment::Variant(variant) => GlyphFragment::new(ctx, variant.c), + MathFragment::Variant(variant) => { + GlyphFragment::new(ctx, variant.c, variant.span) + } _ => return, }; diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs index 0624832a7..f670159d4 100644 --- a/library/src/math/frac.rs +++ b/library/src/math/frac.rs @@ -32,7 +32,7 @@ pub struct FracNode { impl LayoutMath for FracNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.num(), &self.denom(), false) + layout(ctx, &self.num(), &self.denom(), false, self.span()) } } @@ -58,7 +58,7 @@ pub struct BinomNode { impl LayoutMath for BinomNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.upper(), &self.lower(), true) + layout(ctx, &self.upper(), &self.lower(), true, self.span()) } } @@ -68,6 +68,7 @@ fn layout( num: &Content, denom: &Content, binom: bool, + span: Span, ) -> SourceResult<()> { let short_fall = DELIM_SHORT_FALL.scaled(ctx); let axis = scaled!(ctx, axis_height); @@ -121,9 +122,13 @@ fn layout( frame.push_frame(denom_pos, denom); if binom { - ctx.push(GlyphFragment::new(ctx, '(').stretch_vertical(ctx, height, short_fall)); + ctx.push( + GlyphFragment::new(ctx, '(', span).stretch_vertical(ctx, height, short_fall), + ); ctx.push(FrameFragment::new(ctx, frame)); - ctx.push(GlyphFragment::new(ctx, ')').stretch_vertical(ctx, height, short_fall)); + ctx.push( + GlyphFragment::new(ctx, ')', span).stretch_vertical(ctx, height, short_fall), + ); } else { frame.push( line_pos, diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs index de456b82f..87e285554 100644 --- a/library/src/math/fragment.rs +++ b/library/src/math/fragment.rs @@ -147,21 +147,22 @@ pub struct GlyphFragment { pub style: MathStyle, pub font_size: Abs, pub class: Option, + pub span: Span, } impl GlyphFragment { - pub fn new(ctx: &MathContext, c: char) -> Self { + pub fn new(ctx: &MathContext, c: char, span: Span) -> Self { let id = ctx.ttf.glyph_index(c).unwrap_or_default(); - Self::with_id(ctx, c, id) + Self::with_id(ctx, c, id, span) } - pub fn try_new(ctx: &MathContext, c: char) -> Option { + pub fn try_new(ctx: &MathContext, c: char, span: Span) -> Option { let c = ctx.style.styled_char(c); let id = ctx.ttf.glyph_index(c)?; - Some(Self::with_id(ctx, c, id)) + Some(Self::with_id(ctx, c, id, span)) } - pub fn with_id(ctx: &MathContext, c: char, id: GlyphId) -> Self { + pub fn with_id(ctx: &MathContext, c: char, id: GlyphId, span: Span) -> Self { let advance = ctx.ttf.glyph_hor_advance(id).unwrap_or_default(); let italics = italics_correction(ctx, id).unwrap_or_default(); let bbox = ctx.ttf.glyph_bounding_box(id).unwrap_or(Rect { @@ -192,6 +193,7 @@ impl GlyphFragment { ':' => Some(MathClass::Relation), _ => unicode_math_class::class(c), }, + span, } } @@ -208,6 +210,7 @@ impl GlyphFragment { font_size: self.font_size, italics_correction: self.italics_correction, class: self.class, + span: self.span, } } @@ -222,7 +225,7 @@ impl GlyphFragment { c: self.c, x_advance: Em::from_length(self.width, self.font_size), x_offset: Em::zero(), - span: Span::detached(), + span: self.span, offset: 0, }], }; @@ -249,6 +252,7 @@ pub struct VariantFragment { pub style: MathStyle, pub font_size: Abs, pub class: Option, + pub span: Span, } impl Debug for VariantFragment { diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs index 6dec645c0..148d79e8c 100644 --- a/library/src/math/matrix.rs +++ b/library/src/math/matrix.rs @@ -36,7 +36,13 @@ impl LayoutMath for VecNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let delim = self.delim(ctx.styles()); let frame = layout_vec_body(ctx, &self.children(), Align::Center)?; - layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close())) + layout_delimiters( + ctx, + frame, + Some(delim.open()), + Some(delim.close()), + self.span(), + ) } } @@ -112,7 +118,13 @@ impl LayoutMath for MatNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let delim = self.delim(ctx.styles()); let frame = layout_mat_body(ctx, &self.rows())?; - layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close())) + layout_delimiters( + ctx, + frame, + Some(delim.open()), + Some(delim.close()), + self.span(), + ) } } @@ -152,7 +164,7 @@ impl LayoutMath for CasesNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let delim = self.delim(ctx.styles()); let frame = layout_vec_body(ctx, &self.children(), Align::Left)?; - layout_delimiters(ctx, frame, Some(delim.open()), None) + layout_delimiters(ctx, frame, Some(delim.open()), None, self.span()) } } @@ -288,6 +300,7 @@ fn layout_delimiters( mut frame: Frame, left: Option, right: Option, + span: Span, ) -> SourceResult<()> { let axis = scaled!(ctx, axis_height); let short_fall = DELIM_SHORT_FALL.scaled(ctx); @@ -296,14 +309,17 @@ fn layout_delimiters( frame.set_baseline(height / 2.0 + axis); if let Some(left) = left { - ctx.push(GlyphFragment::new(ctx, left).stretch_vertical(ctx, target, short_fall)); + ctx.push( + GlyphFragment::new(ctx, left, span).stretch_vertical(ctx, target, short_fall), + ); } ctx.push(FrameFragment::new(ctx, frame)); if let Some(right) = right { ctx.push( - GlyphFragment::new(ctx, right).stretch_vertical(ctx, target, short_fall), + GlyphFragment::new(ctx, right, span) + .stretch_vertical(ctx, target, short_fall), ); } diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index c11bea792..0cc7f43ca 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -264,7 +264,7 @@ impl LayoutMath for Content { } if let Some(node) = self.to::() { - ctx.layout_text(&node.text())?; + ctx.layout_text(node)?; return Ok(()); } diff --git a/library/src/math/root.rs b/library/src/math/root.rs index cb01e6a17..1ab99b2b6 100644 --- a/library/src/math/root.rs +++ b/library/src/math/root.rs @@ -18,7 +18,7 @@ pub struct SqrtNode { impl LayoutMath for SqrtNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, None, &self.radicand()) + layout(ctx, None, &self.radicand(), self.span()) } } @@ -44,7 +44,7 @@ pub struct RootNode { impl LayoutMath for RootNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, Some(&self.index()), &self.radicand()) + layout(ctx, Some(&self.index()), &self.radicand(), self.span()) } } @@ -55,6 +55,7 @@ fn layout( ctx: &mut MathContext, mut index: Option<&Content>, radicand: &Content, + span: Span, ) -> SourceResult<()> { let gap = scaled!( ctx, @@ -80,7 +81,7 @@ fn layout( frame }) .unwrap_or_else(|| { - let glyph = GlyphFragment::new(ctx, '√'); + let glyph = GlyphFragment::new(ctx, '√', span); glyph.stretch_vertical(ctx, target, Abs::zero()).frame }); @@ -145,7 +146,7 @@ fn precomposed(ctx: &MathContext, index: Option<&Content>, target: Abs) -> Optio }; ctx.ttf.glyph_index(c)?; - let glyph = GlyphFragment::new(ctx, c); + let glyph = GlyphFragment::new(ctx, c, node.span()); let variant = glyph.stretch_vertical(ctx, target, Abs::zero()).frame; if variant.height() < target { return None; diff --git a/library/src/math/stretch.rs b/library/src/math/stretch.rs index 1c9948029..7ba6e5c1a 100644 --- a/library/src/math/stretch.rs +++ b/library/src/math/stretch.rs @@ -73,7 +73,7 @@ fn stretch_glyph( // This is either good or the best we've got. if short_target <= best_advance || construction.assembly.is_none() { - return GlyphFragment::with_id(ctx, base.c, best_id).to_variant(); + return GlyphFragment::with_id(ctx, base.c, best_id, base.span).to_variant(); } // Assemble from parts. @@ -142,7 +142,7 @@ fn assemble( advance += ratio * (max_overlap - min_overlap); } - let fragment = GlyphFragment::with_id(ctx, base.c, part.glyph_id); + let fragment = GlyphFragment::with_id(ctx, base.c, part.glyph_id, base.span); selected.push((fragment, advance)); } @@ -181,6 +181,7 @@ fn assemble( font_size: base.font_size, italics_correction: Abs::zero(), class: base.class, + span: base.span, } } diff --git a/library/src/math/underover.rs b/library/src/math/underover.rs index a723ae978..cfbb30b65 100644 --- a/library/src/math/underover.rs +++ b/library/src/math/underover.rs @@ -22,7 +22,7 @@ pub struct UnderlineNode { impl LayoutMath for UnderlineNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &None, '\u{305}', LINE_GAP, false) + layout(ctx, &self.body(), &None, '\u{305}', LINE_GAP, false, self.span()) } } @@ -44,7 +44,7 @@ pub struct OverlineNode { impl LayoutMath for OverlineNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &None, '\u{332}', LINE_GAP, true) + layout(ctx, &self.body(), &None, '\u{332}', LINE_GAP, true, self.span()) } } @@ -70,7 +70,15 @@ pub struct UnderbraceNode { impl LayoutMath for UnderbraceNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⏟', BRACE_GAP, false) + layout( + ctx, + &self.body(), + &self.annotation(ctx.styles()), + '⏟', + BRACE_GAP, + false, + self.span(), + ) } } @@ -96,7 +104,15 @@ pub struct OverbraceNode { impl LayoutMath for OverbraceNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⏞', BRACE_GAP, true) + layout( + ctx, + &self.body(), + &self.annotation(ctx.styles()), + '⏞', + BRACE_GAP, + true, + self.span(), + ) } } @@ -122,7 +138,15 @@ pub struct UnderbracketNode { impl LayoutMath for UnderbracketNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⎵', BRACKET_GAP, false) + layout( + ctx, + &self.body(), + &self.annotation(ctx.styles()), + '⎵', + BRACKET_GAP, + false, + self.span(), + ) } } @@ -148,7 +172,15 @@ pub struct OverbracketNode { impl LayoutMath for OverbracketNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⎴', BRACKET_GAP, true) + layout( + ctx, + &self.body(), + &self.annotation(ctx.styles()), + '⎴', + BRACKET_GAP, + true, + self.span(), + ) } } @@ -160,10 +192,11 @@ fn layout( c: char, gap: Em, reverse: bool, + span: Span, ) -> SourceResult<()> { let gap = gap.scaled(ctx); let body = ctx.layout_row(body)?; - let glyph = GlyphFragment::new(ctx, c); + let glyph = GlyphFragment::new(ctx, c, span); let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); let mut rows = vec![body, stretched.into()]; diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs index e82f80c6c..470479274 100644 --- a/library/src/meta/heading.rs +++ b/library/src/meta/heading.rs @@ -107,6 +107,7 @@ impl Synthesize for HeadingNode { let node = self .clone() + .with_level(self.level(styles)) .with_outlined(self.outlined(styles)) .with_numbers(numbering.is_some().then(|| counter.take())) .with_numbering(numbering) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index fe56c0607..ae5f668a7 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -368,7 +368,7 @@ fn eval_markup( *node = mem::take(node).labelled(label); } } - value => seq.push(value.display()), + value => seq.push(value.display().spanned(expr.span())), }, } @@ -458,6 +458,12 @@ impl Eval for ast::Expr { } } +impl ast::Expr { + fn eval_display(&self, vm: &mut Vm) -> SourceResult { + Ok(self.eval(vm)?.display().spanned(self.span())) + } +} + impl Eval for ast::Text { type Output = Content; @@ -619,10 +625,9 @@ impl Eval for ast::Math { fn eval(&self, vm: &mut Vm) -> SourceResult { Ok(Content::sequence( self.exprs() - .map(|expr| Ok(expr.eval(vm)?.display())) + .map(|expr| expr.eval_display(vm)) .collect::>()?, - ) - .spanned(self.span())) + )) } } @@ -646,9 +651,9 @@ impl Eval for ast::MathDelimited { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - let open = self.open().eval(vm)?.display(); + let open = self.open().eval_display(vm)?; let body = self.body().eval(vm)?; - let close = self.close().eval(vm)?.display(); + let close = self.close().eval_display(vm)?; Ok((vm.items.math_delimited)(open, body, close)) } } @@ -657,12 +662,9 @@ impl Eval for ast::MathAttach { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - let base = self.base().eval(vm)?.display(); - let bottom = self - .bottom() - .map(|expr| expr.eval(vm).map(Value::display)) - .transpose()?; - let top = self.top().map(|expr| expr.eval(vm).map(Value::display)).transpose()?; + let base = self.base().eval_display(vm)?; + let bottom = self.bottom().map(|expr| expr.eval_display(vm)).transpose()?; + let top = self.top().map(|expr| expr.eval_display(vm)).transpose()?; Ok((vm.items.math_attach)(base, bottom, top)) } } @@ -671,8 +673,8 @@ impl Eval for ast::MathFrac { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - let num = self.num().eval(vm)?.display(); - let denom = self.denom().eval(vm)?.display(); + let num = self.num().eval_display(vm)?; + let denom = self.denom().eval_display(vm)?; Ok((vm.items.math_frac)(num, denom)) } } @@ -1038,7 +1040,7 @@ impl Eval for ast::FuncCall { body += arg; } return Ok(Value::Content( - callee.display() + callee.display().spanned(callee_span) + (vm.items.math_delimited)( (vm.items.text)('('.into()), body, diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 24b3431f2..1fb40204d 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -15,7 +15,7 @@ use typst::diag::{bail, FileError, FileResult}; use typst::doc::{Document, Element, Frame, Meta}; use typst::eval::{func, Library, Value}; use typst::font::{Font, FontBook}; -use typst::geom::{Abs, RgbaColor, Sides, Smart}; +use typst::geom::{Abs, Color, RgbaColor, Sides, Smart}; use typst::syntax::{Source, SourceId, Span, SyntaxNode}; use typst::util::{Buffer, PathExt}; use typst::World;