Spans in math

This commit is contained in:
Laurenz 2023-03-12 12:53:50 +01:00
parent 2720a44b96
commit ad05ba5925
13 changed files with 118 additions and 51 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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,
};

View File

@ -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,

View File

@ -147,21 +147,22 @@ pub struct GlyphFragment {
pub style: MathStyle,
pub font_size: Abs,
pub class: Option<MathClass>,
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<Self> {
pub fn try_new(ctx: &MathContext, c: char, span: Span) -> Option<Self> {
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<MathClass>,
pub span: Span,
}
impl Debug for VariantFragment {

View File

@ -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<char>,
right: Option<char>,
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),
);
}

View File

@ -264,7 +264,7 @@ impl LayoutMath for Content {
}
if let Some(node) = self.to::<TextNode>() {
ctx.layout_text(&node.text())?;
ctx.layout_text(node)?;
return Ok(());
}

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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()];

View File

@ -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)

View File

@ -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<Content> {
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<Self::Output> {
Ok(Content::sequence(
self.exprs()
.map(|expr| Ok(expr.eval(vm)?.display()))
.map(|expr| expr.eval_display(vm))
.collect::<SourceResult<_>>()?,
)
.spanned(self.span()))
))
}
}
@ -646,9 +651,9 @@ impl Eval for ast::MathDelimited {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
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<Self::Output> {
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<Self::Output> {
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,

View File

@ -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;