mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Spans in math
This commit is contained in:
parent
2720a44b96
commit
ad05ba5925
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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(());
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()];
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user