parent
6c542ea1a4
commit
03675fc429
@ -77,18 +77,9 @@ impl LayoutMath for AttachElem {
|
|||||||
let b = layout_attachment(ctx, Self::b)?;
|
let b = layout_attachment(ctx, Self::b)?;
|
||||||
ctx.unstyle();
|
ctx.unstyle();
|
||||||
|
|
||||||
let as_limits = self.base().is::<LimitsElem>()
|
let limits = base.limits().active(ctx);
|
||||||
|| (!self.base().is::<ScriptsElem>()
|
let (t, tr) = if limits || tr.is_some() { (t, tr) } else { (None, t) };
|
||||||
&& ctx.style.size == MathSize::Display
|
let (b, br) = if limits || br.is_some() { (b, br) } else { (None, b) };
|
||||||
&& base.class() == Some(MathClass::Large)
|
|
||||||
&& match &base {
|
|
||||||
MathFragment::Variant(variant) => LIMITS.contains(&variant.c),
|
|
||||||
MathFragment::Frame(fragment) => fragment.limits,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let (t, tr) = if as_limits || tr.is_some() { (t, tr) } else { (None, t) };
|
|
||||||
let (b, br) = if as_limits || br.is_some() { (b, br) } else { (None, b) };
|
|
||||||
layout_attachments(ctx, base, [tl, t, tr, bl, b, br])
|
layout_attachments(ctx, base, [tl, t, tr, bl, b, br])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +103,10 @@ pub struct ScriptsElem {
|
|||||||
impl LayoutMath for ScriptsElem {
|
impl LayoutMath for ScriptsElem {
|
||||||
#[tracing::instrument(skip(ctx))]
|
#[tracing::instrument(skip(ctx))]
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
self.body().layout_math(ctx)
|
let mut fragment = ctx.layout_fragment(&self.body())?;
|
||||||
|
fragment.set_limits(Limits::Never);
|
||||||
|
ctx.push(fragment);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +129,56 @@ pub struct LimitsElem {
|
|||||||
impl LayoutMath for LimitsElem {
|
impl LayoutMath for LimitsElem {
|
||||||
#[tracing::instrument(skip(ctx))]
|
#[tracing::instrument(skip(ctx))]
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
self.body().layout_math(ctx)
|
let mut fragment = ctx.layout_fragment(&self.body())?;
|
||||||
|
fragment.set_limits(Limits::Always);
|
||||||
|
ctx.push(fragment);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describes in which situation a frame should use limits for attachments.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Limits {
|
||||||
|
/// Always scripts.
|
||||||
|
Never,
|
||||||
|
/// Display limits only in `display` math.
|
||||||
|
Display,
|
||||||
|
/// Always limits.
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Limits {
|
||||||
|
/// The default limit configuration if the given character is the base.
|
||||||
|
pub fn for_char(c: char) -> Self {
|
||||||
|
if Self::DEFAULT_TO_LIMITS.contains(&c) {
|
||||||
|
Limits::Display
|
||||||
|
} else {
|
||||||
|
Limits::Never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether limits should be displayed in this context
|
||||||
|
pub fn active(&self, ctx: &MathContext) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Always => true,
|
||||||
|
Self::Display => ctx.style.size == MathSize::Display,
|
||||||
|
Self::Never => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unicode codepoints that should show attachments as limits in display
|
||||||
|
/// mode.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
const DEFAULT_TO_LIMITS: &[char] = &[
|
||||||
|
/* ∏ */ '\u{220F}', /* ∐ */ '\u{2210}', /* ∑ */ '\u{2211}',
|
||||||
|
/* ⋀ */ '\u{22C0}', /* ⋁ */ '\u{22C1}',
|
||||||
|
/* ⋂ */ '\u{22C2}', /* ⋃ */ '\u{22C3}',
|
||||||
|
/* ⨀ */ '\u{2A00}', /* ⨁ */ '\u{2A01}', /* ⨂ */ '\u{2A02}',
|
||||||
|
/* ⨃ */ '\u{2A03}', /* ⨄ */ '\u{2A04}',
|
||||||
|
/* ⨅ */ '\u{2A05}', /* ⨆ */ '\u{2A06}',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! measure {
|
macro_rules! measure {
|
||||||
($e: ident, $attr: ident) => {
|
($e: ident, $attr: ident) => {
|
||||||
$e.as_ref().map(|e| e.$attr()).unwrap_or_default()
|
$e.as_ref().map(|e| e.$attr()).unwrap_or_default()
|
||||||
@ -358,14 +398,3 @@ fn is_atomic_text_frame(frame: &Frame) -> bool {
|
|||||||
.filter(|item| !matches!(item, FrameItem::Meta(_, _)));
|
.filter(|item| !matches!(item, FrameItem::Meta(_, _)));
|
||||||
matches!(iter.next(), Some(FrameItem::Text(_))) && iter.next().is_none()
|
matches!(iter.next(), Some(FrameItem::Text(_))) && iter.next().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unicode codepoints that should have sub- and superscripts attached as limits.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
const LIMITS: &[char] = &[
|
|
||||||
/* ∏ */ '\u{220F}', /* ∐ */ '\u{2210}', /* ∑ */ '\u{2211}',
|
|
||||||
/* ⋀ */ '\u{22C0}', /* ⋁ */ '\u{22C1}',
|
|
||||||
/* ⋂ */ '\u{22C2}', /* ⋃ */ '\u{22C3}',
|
|
||||||
/* ⨀ */ '\u{2A00}', /* ⨁ */ '\u{2A01}', /* ⨂ */ '\u{2A02}',
|
|
||||||
/* ⨃ */ '\u{2A03}', /* ⨄ */ '\u{2A04}',
|
|
||||||
/* ⨅ */ '\u{2A05}', /* ⨆ */ '\u{2A06}',
|
|
||||||
];
|
|
||||||
|
@ -90,6 +90,15 @@ impl MathFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_limits(&mut self, limits: Limits) {
|
||||||
|
match self {
|
||||||
|
Self::Glyph(glyph) => glyph.limits = limits,
|
||||||
|
Self::Variant(variant) => variant.limits = limits,
|
||||||
|
Self::Frame(fragment) => fragment.limits = limits,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_spaced(&self) -> bool {
|
pub fn is_spaced(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
MathFragment::Frame(frame) => frame.spaced,
|
MathFragment::Frame(frame) => frame.spaced,
|
||||||
@ -113,6 +122,15 @@ impl MathFragment {
|
|||||||
_ => Frame::new(self.size()),
|
_ => Frame::new(self.size()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn limits(&self) -> Limits {
|
||||||
|
match self {
|
||||||
|
MathFragment::Glyph(glyph) => glyph.limits,
|
||||||
|
MathFragment::Variant(variant) => variant.limits,
|
||||||
|
MathFragment::Frame(fragment) => fragment.limits,
|
||||||
|
_ => Limits::Never,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<GlyphFragment> for MathFragment {
|
impl From<GlyphFragment> for MathFragment {
|
||||||
@ -149,6 +167,7 @@ pub struct GlyphFragment {
|
|||||||
pub class: Option<MathClass>,
|
pub class: Option<MathClass>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub meta: Vec<Meta>,
|
pub meta: Vec<Meta>,
|
||||||
|
pub limits: Limits,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlyphFragment {
|
impl GlyphFragment {
|
||||||
@ -164,6 +183,10 @@ impl GlyphFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_id(ctx: &MathContext, c: char, id: GlyphId, span: Span) -> Self {
|
pub fn with_id(ctx: &MathContext, c: char, id: GlyphId, span: Span) -> Self {
|
||||||
|
let class = match c {
|
||||||
|
':' => Some(MathClass::Relation),
|
||||||
|
_ => unicode_math_class::class(c),
|
||||||
|
};
|
||||||
let mut fragment = Self {
|
let mut fragment = Self {
|
||||||
id,
|
id,
|
||||||
c,
|
c,
|
||||||
@ -175,11 +198,9 @@ impl GlyphFragment {
|
|||||||
width: Abs::zero(),
|
width: Abs::zero(),
|
||||||
ascent: Abs::zero(),
|
ascent: Abs::zero(),
|
||||||
descent: Abs::zero(),
|
descent: Abs::zero(),
|
||||||
|
limits: Limits::for_char(c),
|
||||||
italics_correction: Abs::zero(),
|
italics_correction: Abs::zero(),
|
||||||
class: match c {
|
class,
|
||||||
':' => Some(MathClass::Relation),
|
|
||||||
_ => unicode_math_class::class(c),
|
|
||||||
},
|
|
||||||
span,
|
span,
|
||||||
meta: MetaElem::data_in(ctx.styles()),
|
meta: MetaElem::data_in(ctx.styles()),
|
||||||
};
|
};
|
||||||
@ -224,6 +245,7 @@ impl GlyphFragment {
|
|||||||
italics_correction: self.italics_correction,
|
italics_correction: self.italics_correction,
|
||||||
class: self.class,
|
class: self.class,
|
||||||
span: self.span,
|
span: self.span,
|
||||||
|
limits: self.limits,
|
||||||
frame: self.into_frame(),
|
frame: self.into_frame(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,6 +290,7 @@ pub struct VariantFragment {
|
|||||||
pub font_size: Abs,
|
pub font_size: Abs,
|
||||||
pub class: Option<MathClass>,
|
pub class: Option<MathClass>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
pub limits: Limits,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for VariantFragment {
|
impl Debug for VariantFragment {
|
||||||
@ -282,7 +305,7 @@ pub struct FrameFragment {
|
|||||||
pub style: MathStyle,
|
pub style: MathStyle,
|
||||||
pub font_size: Abs,
|
pub font_size: Abs,
|
||||||
pub class: MathClass,
|
pub class: MathClass,
|
||||||
pub limits: bool,
|
pub limits: Limits,
|
||||||
pub spaced: bool,
|
pub spaced: bool,
|
||||||
pub base_ascent: Abs,
|
pub base_ascent: Abs,
|
||||||
}
|
}
|
||||||
@ -296,7 +319,7 @@ impl FrameFragment {
|
|||||||
font_size: ctx.size,
|
font_size: ctx.size,
|
||||||
style: ctx.style,
|
style: ctx.style,
|
||||||
class: MathClass::Normal,
|
class: MathClass::Normal,
|
||||||
limits: false,
|
limits: Limits::Never,
|
||||||
spaced: false,
|
spaced: false,
|
||||||
base_ascent,
|
base_ascent,
|
||||||
}
|
}
|
||||||
@ -306,7 +329,7 @@ impl FrameFragment {
|
|||||||
Self { class, ..self }
|
Self { class, ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_limits(self, limits: bool) -> Self {
|
pub fn with_limits(self, limits: Limits) -> Self {
|
||||||
Self { limits, ..self }
|
Self { limits, ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub struct OpElem {
|
|||||||
#[required]
|
#[required]
|
||||||
pub text: EcoString,
|
pub text: EcoString,
|
||||||
|
|
||||||
/// Whether the operator should force attachments to display as limits.
|
/// Whether the operator should show attachments as limits in display mode.
|
||||||
#[default(false)]
|
#[default(false)]
|
||||||
pub limits: bool,
|
pub limits: bool,
|
||||||
}
|
}
|
||||||
@ -39,7 +39,11 @@ impl LayoutMath for OpElem {
|
|||||||
ctx.push(
|
ctx.push(
|
||||||
FrameFragment::new(ctx, fragment.into_frame())
|
FrameFragment::new(ctx, fragment.into_frame())
|
||||||
.with_class(MathClass::Large)
|
.with_class(MathClass::Large)
|
||||||
.with_limits(self.limits(ctx.styles())),
|
.with_limits(if self.limits(ctx.styles()) {
|
||||||
|
Limits::Display
|
||||||
|
} else {
|
||||||
|
Limits::Never
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,7 @@ fn assemble(
|
|||||||
italics_correction: Abs::zero(),
|
italics_correction: Abs::zero(),
|
||||||
class: base.class,
|
class: base.class,
|
||||||
span: base.span,
|
span: base.span,
|
||||||
|
limits: base.limits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
@ -91,3 +91,11 @@ $ limits(integral)_a^b != integral_a^b $
|
|||||||
---
|
---
|
||||||
// Error: 30-34 unknown variable: oops
|
// Error: 30-34 unknown variable: oops
|
||||||
$ attach(A, t: #locate(it => oops)) $
|
$ attach(A, t: #locate(it => oops)) $
|
||||||
|
|
||||||
|
---
|
||||||
|
// Show and let rules for limits and scripts
|
||||||
|
#let eq = $ ∫_a^b iota_a^b $
|
||||||
|
#eq
|
||||||
|
#show "∫": math.limits
|
||||||
|
#show math.iota: math.limits
|
||||||
|
#eq
|
||||||
|
Loading…
x
Reference in New Issue
Block a user