diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs index fafb96f22..d682746e2 100644 --- a/library/src/math/ctx.rs +++ b/library/src/math/ctx.rs @@ -23,7 +23,7 @@ macro_rules! percent { } /// The context for math layout. -pub(super) struct MathContext<'a, 'b, 'v> { +pub struct MathContext<'a, 'b, 'v> { pub vt: &'v mut Vt<'b>, pub regions: Regions<'a>, pub font: &'a Font, diff --git a/library/src/math/lr.rs b/library/src/math/delimited.rs similarity index 100% rename from library/src/math/lr.rs rename to library/src/math/delimited.rs diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs index ebdb5c026..9f7fb9d0d 100644 --- a/library/src/math/frac.rs +++ b/library/src/math/frac.rs @@ -1,5 +1,7 @@ use super::*; +const FRAC_AROUND: Em = Em::new(0.1); + /// # Fraction /// A mathematical fraction. /// @@ -130,7 +132,7 @@ fn layout( let denom = ctx.layout_frame(denom)?; ctx.unstyle(); - let around = Em::new(0.1).scaled(ctx); + let around = FRAC_AROUND.scaled(ctx); let num_gap = (shift_up - axis - num.descent()).max(num_min + thickness / 2.0); let denom_gap = (shift_down + axis - denom.ascent()).max(denom_min + thickness / 2.0); diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs index fe9642a20..bef3f5780 100644 --- a/library/src/math/fragment.rs +++ b/library/src/math/fragment.rs @@ -1,7 +1,7 @@ use super::*; #[derive(Debug, Clone)] -pub(super) enum MathFragment { +pub enum MathFragment { Glyph(GlyphFragment), Variant(VariantFragment), Frame(FrameFragment), @@ -118,7 +118,7 @@ impl From for MathFragment { } #[derive(Debug, Clone, Copy)] -pub(super) struct GlyphFragment { +pub struct GlyphFragment { pub id: GlyphId, pub c: char, pub lang: Lang, diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs index 527cf3159..5f448901b 100644 --- a/library/src/math/matrix.rs +++ b/library/src/math/matrix.rs @@ -1,7 +1,7 @@ use super::*; const ROW_GAP: Em = Em::new(0.5); -const COL_GAP: Em = Em::new(0.75); +const COL_GAP: Em = Em::new(0.5); const VERTICAL_PADDING: Ratio = Ratio::new(0.1); /// # Vector diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index ab1fab136..4e60faca7 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -5,29 +5,29 @@ mod ctx; mod accent; mod align; mod attach; +mod delimited; mod frac; mod fragment; -mod lr; mod matrix; mod op; mod root; mod row; mod spacing; -mod stack; mod stretch; mod style; mod symbols; +mod underover; pub use self::accent::*; pub use self::align::*; pub use self::attach::*; +pub use self::delimited::*; pub use self::frac::*; -pub use self::lr::*; pub use self::matrix::*; pub use self::op::*; pub use self::root::*; -pub use self::stack::*; pub use self::style::*; +pub use self::underover::*; use ttf_parser::{GlyphId, Rect}; use typst::font::Font; @@ -230,7 +230,7 @@ impl Layout for FormulaNode { impl Inline for FormulaNode {} #[capability] -trait LayoutMath { +pub trait LayoutMath { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()>; } diff --git a/library/src/math/root.rs b/library/src/math/root.rs index d348ecc16..2d27bb114 100644 --- a/library/src/math/root.rs +++ b/library/src/math/root.rs @@ -1,5 +1,7 @@ use super::*; +const MIN_INDEX_SHIFT: Em = Em::new(0.35); + /// # Square Root /// A square root. /// @@ -126,7 +128,7 @@ fn layout( if let Some(index) = &index { sqrt_offset = kern_before + index.width() + kern_after; - shift_up.set_max(index.descent() + Em::new(0.35).scaled(ctx)); + shift_up.set_max(index.descent() + MIN_INDEX_SHIFT.scaled(ctx)); ascent.set_max(shift_up + index.ascent()); } diff --git a/library/src/math/row.rs b/library/src/math/row.rs index d971ce221..e66fc18e8 100644 --- a/library/src/math/row.rs +++ b/library/src/math/row.rs @@ -2,8 +2,10 @@ use crate::layout::AlignNode; use super::*; +pub const TIGHT_LEADING: Em = Em::new(0.25); + #[derive(Debug, Default, Clone)] -pub(super) struct MathRow(pub Vec); +pub struct MathRow(pub Vec); impl MathRow { pub fn new() -> Self { @@ -85,8 +87,12 @@ impl MathRow { ) -> Frame { if self.0.iter().any(|frag| matches!(frag, MathFragment::Linebreak)) { let fragments = std::mem::take(&mut self.0); + let leading = if ctx.style.size >= MathSize::Text { + ctx.styles().get(ParNode::LEADING) + } else { + TIGHT_LEADING.scaled(ctx) + }; - let leading = ctx.styles().get(ParNode::LEADING) * ctx.style.size.factor(ctx); let rows: Vec<_> = fragments .split(|frag| matches!(frag, MathFragment::Linebreak)) .map(|slice| Self(slice.to_vec())) diff --git a/library/src/math/spacing.rs b/library/src/math/spacing.rs index 7083c5e10..fad1c863a 100644 --- a/library/src/math/spacing.rs +++ b/library/src/math/spacing.rs @@ -24,11 +24,8 @@ pub(super) fn spacing( ) -> Em { use MathClass::*; let script = style.size <= MathSize::Script; - let (Some(l), Some(r)) = (left.class(), right.class()) else { - return ZERO; - }; - - match (l, r) { + let class = |frag: &MathFragment| frag.class().unwrap_or(Special); + match (class(left), class(right)) { // No spacing before punctuation; thin spacing after punctuation, unless // in script size. (_, Punctuation) => ZERO, diff --git a/library/src/math/style.rs b/library/src/math/style.rs index b3f3d8df7..da2e2313e 100644 --- a/library/src/math/style.rs +++ b/library/src/math/style.rs @@ -443,10 +443,6 @@ pub(super) fn styled_char(style: MathStyle, c: char) -> char { '∂' | 'ϵ' | 'ϑ' | 'ϰ' | 'ϕ' | 'ϱ' | 'ϖ' )); - if c == '-' { - return '−'; - } - if let Some(c) = latin_exception(c, variant, bold, italic) { return c; } diff --git a/library/src/math/stack.rs b/library/src/math/underover.rs similarity index 100% rename from library/src/math/stack.rs rename to library/src/math/underover.rs diff --git a/src/model/eval.rs b/src/model/eval.rs index 6e03d4405..1fbf41254 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -919,32 +919,26 @@ impl Eval for ast::FuncCall { // field access and does not evaluate to a module. let (callee, mut args) = if let ast::Expr::FieldAccess(access) = callee { let target = access.target(); - let method = access.field(); - let method_span = method.span(); - let method = method.take(); - let point = || Tracepoint::Call(Some(method.clone())); - if methods::is_mutating(&method) { + let field = access.field(); + let field_span = field.span(); + let field = field.take(); + let point = || Tracepoint::Call(Some(field.clone())); + if methods::is_mutating(&field) { let args = args.eval(vm)?; - let value = target.access(vm)?; - - let value = if let Value::Module(module) = &value { - module.get(&method).cloned().at(method_span)? - } else { - return methods::call_mut(value, &method, args, span) + let target = target.access(vm)?; + if !matches!(target, Value::Symbol(_) | Value::Module(_)) { + return methods::call_mut(target, &field, args, span) .trace(vm.world, point, span); - }; - - (value, args) + } + (target.field(&field).at(field_span)?, args) } else { let target = target.eval(vm)?; let args = args.eval(vm)?; - let value = if let Value::Module(module) = &target { - module.get(&method).cloned().at(method_span)? - } else { - return methods::call(vm, target, &method, args, span) + if !matches!(target, Value::Symbol(_) | Value::Module(_)) { + return methods::call(vm, target, &field, args, span) .trace(vm.world, point, span); - }; - (value, args) + } + (target.field(&field).at(field_span)?, args) } } else { (callee.eval(vm)?, args.eval(vm)?) diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 1cc29b04b..e844f6222 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -426,11 +426,14 @@ node! { impl Shorthand { /// A list of all shorthands. pub const LIST: &[(&'static str, char)] = &[ + // Text only. ("~", '\u{00A0}'), ("--", '\u{2013}'), ("---", '\u{2014}'), ("-?", '\u{00AD}'), - ("...", '…'), + // Math only. + ("-", '\u{2212}'), + ("'", '′'), ("*", '∗'), ("!=", '≠'), ("<<", '≪'), @@ -450,6 +453,8 @@ impl Shorthand { ("[|", '⟦'), ("|]", '⟧'), ("||", '‖'), + // Both. + ("...", '…'), ]; /// Get the shorthanded character. diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index 471e437a5..0bf7966fd 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -380,17 +380,16 @@ impl Lexer<'_> { '\\' => self.backslash(), '"' => self.string(), - '*' => SyntaxKind::Shorthand, '.' if self.s.eat_if("..") => SyntaxKind::Shorthand, '|' if self.s.eat_if("->") => SyntaxKind::Shorthand, '|' if self.s.eat_if("=>") => SyntaxKind::Shorthand, - '!' if self.s.eat_if('=') => SyntaxKind::Shorthand, '<' if self.s.eat_if("<<") => SyntaxKind::Shorthand, - '<' if self.s.eat_if('<') => SyntaxKind::Shorthand, '>' if self.s.eat_if(">>") => SyntaxKind::Shorthand, - '>' if self.s.eat_if('>') => SyntaxKind::Shorthand, '<' if self.s.eat_if("=>") => SyntaxKind::Shorthand, '<' if self.s.eat_if("->") => SyntaxKind::Shorthand, + '!' if self.s.eat_if('=') => SyntaxKind::Shorthand, + '<' if self.s.eat_if('<') => SyntaxKind::Shorthand, + '>' if self.s.eat_if('>') => SyntaxKind::Shorthand, '<' if self.s.eat_if('=') => SyntaxKind::Shorthand, '>' if self.s.eat_if('=') => SyntaxKind::Shorthand, '<' if self.s.eat_if('-') => SyntaxKind::Shorthand, @@ -400,6 +399,7 @@ impl Lexer<'_> { '[' if self.s.eat_if('|') => SyntaxKind::Shorthand, '|' if self.s.eat_if(']') => SyntaxKind::Shorthand, '|' if self.s.eat_if('|') => SyntaxKind::Shorthand, + '*' | '\'' | '-' => SyntaxKind::Shorthand, '#' => SyntaxKind::Hashtag, '_' => SyntaxKind::Underscore, diff --git a/tests/ref/math/accent.png b/tests/ref/math/accent.png new file mode 100644 index 000000000..c67e0912d Binary files /dev/null and b/tests/ref/math/accent.png differ diff --git a/tests/ref/math/accents.png b/tests/ref/math/accents.png deleted file mode 100644 index cb167533d..000000000 Binary files a/tests/ref/math/accents.png and /dev/null differ diff --git a/tests/ref/math/attach.png b/tests/ref/math/attach.png new file mode 100644 index 000000000..27843eb43 Binary files /dev/null and b/tests/ref/math/attach.png differ diff --git a/tests/ref/math/cases.png b/tests/ref/math/cases.png new file mode 100644 index 000000000..87c358da6 Binary files /dev/null and b/tests/ref/math/cases.png differ diff --git a/tests/ref/math/content.png b/tests/ref/math/content.png new file mode 100644 index 000000000..5d222f929 Binary files /dev/null and b/tests/ref/math/content.png differ diff --git a/tests/ref/math/delimited.png b/tests/ref/math/delimited.png new file mode 100644 index 000000000..ea3ab6c29 Binary files /dev/null and b/tests/ref/math/delimited.png differ diff --git a/tests/ref/math/frac.png b/tests/ref/math/frac.png new file mode 100644 index 000000000..a3a9a3ae1 Binary files /dev/null and b/tests/ref/math/frac.png differ diff --git a/tests/ref/math/matrix.png b/tests/ref/math/matrix.png index 56a4db9c3..4a60b3ff1 100644 Binary files a/tests/ref/math/matrix.png and b/tests/ref/math/matrix.png differ diff --git a/tests/ref/math/multiline.png b/tests/ref/math/multiline.png new file mode 100644 index 000000000..d8b1d8470 Binary files /dev/null and b/tests/ref/math/multiline.png differ diff --git a/tests/ref/math/op.png b/tests/ref/math/op.png new file mode 100644 index 000000000..870250b7e Binary files /dev/null and b/tests/ref/math/op.png differ diff --git a/tests/ref/math/root.png b/tests/ref/math/root.png new file mode 100644 index 000000000..8d9063949 Binary files /dev/null and b/tests/ref/math/root.png differ diff --git a/tests/ref/math/spacing.png b/tests/ref/math/spacing.png new file mode 100644 index 000000000..fcd0d6608 Binary files /dev/null and b/tests/ref/math/spacing.png differ diff --git a/tests/ref/math/style.png b/tests/ref/math/style.png index d9a71807f..04a282088 100644 Binary files a/tests/ref/math/style.png and b/tests/ref/math/style.png differ diff --git a/tests/ref/math/syntax.png b/tests/ref/math/syntax.png index 5a58df56d..442e8bfec 100644 Binary files a/tests/ref/math/syntax.png and b/tests/ref/math/syntax.png differ diff --git a/tests/ref/math/underover.png b/tests/ref/math/underover.png new file mode 100644 index 000000000..a585f59dd Binary files /dev/null and b/tests/ref/math/underover.png differ diff --git a/tests/ref/math/vec.png b/tests/ref/math/vec.png new file mode 100644 index 000000000..20db15122 Binary files /dev/null and b/tests/ref/math/vec.png differ diff --git a/tests/typ/math/accent.typ b/tests/typ/math/accent.typ new file mode 100644 index 000000000..65ef9d1b5 --- /dev/null +++ b/tests/typ/math/accent.typ @@ -0,0 +1,22 @@ +// Test math accents. + +--- +// Test function call. +$grave(a), acute(b), hat(f), tilde(§), macron(ä), diaer(a), ä, \ + breve(\&), dot(!), circle(a), caron(@), arrow(Z), arrow.l(Z)$ + +--- +// Test `accent` function. +$accent(ö, .), accent(v, <-), accent(ZZ, \u{0303})$ + +--- +// Test accent bounds. +$sqrt(tilde(T)) + hat(f)/hat(g)$ + +--- +// Test wide base. +$arrow("ABC" + d), tilde(sum)$ + +--- +// Test high base. +$ tilde(integral), tilde(integral)_a^b, tilde(integral_a^b) $ diff --git a/tests/typ/math/accents.typ b/tests/typ/math/accents.typ deleted file mode 100644 index 284e86f3d..000000000 --- a/tests/typ/math/accents.typ +++ /dev/null @@ -1,15 +0,0 @@ -// Test math accents. - ---- -#set page(width: auto) - -$ grave(a), - acute(a), - hat(a), - tilde(a), - macron(a), - breve(a), - dot(a), - diaer(a), - caron(a), - arrow(a) $ diff --git a/tests/typ/math/attach.typ b/tests/typ/math/attach.typ new file mode 100644 index 000000000..cf3e9521b --- /dev/null +++ b/tests/typ/math/attach.typ @@ -0,0 +1,34 @@ +// Test top and bottom attachments. + +--- +// Test basics. +$f_x + t^b + V_1^2 + + attach(A, top: alpha, bottom: beta)$ + +--- +// Test text vs ident parsing. +$pi_1(Y), a_f(x) != a_zeta(x)$ + +--- +// Test associativity and scaling. +$ 1/(V^2^3^4^5) $ + +--- +// Test high subscript and superscript. +$sqrt(a_(1/2)^zeta)$ +$sqrt(a_alpha^(1/2))$ +$sqrt(a_(1/2)^(3/4))$ + +--- +// Test frame base. +$ (-1)^n + (1/2 + 3)^(-1/2) $ + +--- +// Test limit. +$ lim_(n->infty \ n "grows") sum_(k=0 \ k in NN)^n k $ + +--- +// Test forcing scripts and limits. +$ limits(A)_1^2 != A_1^2 $ +$ scripts(sum)_1^2 != sum_1^2 $ +$ limits(integral)_a^b != integral_a^b $ diff --git a/tests/typ/math/cases.typ b/tests/typ/math/cases.typ new file mode 100644 index 000000000..d591ae50e --- /dev/null +++ b/tests/typ/math/cases.typ @@ -0,0 +1,9 @@ +// Test case distinction. + +--- +$ f(x, y) := cases( + 1 quad &"if" (x dot y)/2 <= 0, + 2 &"if" x divides 2, + 3 &"if" x in NN, + 4 &"else", +) $ diff --git a/tests/typ/math/content.typ b/tests/typ/math/content.typ new file mode 100644 index 000000000..271e00a52 --- /dev/null +++ b/tests/typ/math/content.typ @@ -0,0 +1,14 @@ +// Test arbitrary content in math. + +--- +// Test images and font fallback. +#let monkey = move(dy: 0.2em, image("/res/monkey.svg", height: 1em)) +$ sum_(i=#emoji.apple)^#emoji.apple.red i + monkey/2 $ + +--- +// Test table above fraction. +$ x := #table(columns: 2)[x][y]/mat(1, 2, 3) $ + +--- +// Test non-formula math directly in content. +#math.attach($a$, top: [b]) diff --git a/tests/typ/math/delimited.typ b/tests/typ/math/delimited.typ new file mode 100644 index 000000000..d22b76c0c --- /dev/null +++ b/tests/typ/math/delimited.typ @@ -0,0 +1,38 @@ +// Test delimiter matching and scaling. + +--- +// Test automatic matching. +$ (a) + {b/2} + |a|/2 + (b) $ +$f(x/2) < zeta(c^2 + |a + b/2|)$ + +--- +// Test unmatched. +$[1,2[ = [1,2) != zeta\(x/2\) $ + +--- +// Test manual matching. +$ [|a/b|] != lr(|]a/b|]) != [a/b) $ +$ lr(| ]1,2\[ + 1/2|) $ + +--- +// Test fence confusion. +$ |x + |y| + z/a| \ + |x + lr(|y|) + z/a| $ + +--- +// Test that symbols aren't matched automatically. +$ bracket.l a/b bracket.r + = lr(bracket.l a/b bracket.r) $ + +--- +// Test half LRs. +$ lr(a/b\]) = a = lr(\{a/b) $ + +--- +// Test manual scaling. +$ lr(]sum_(x=1)^n x], size: #70%) + < lr((1, 2), size: #200%) $ + +--- +// Test predefined delimiter pairings. +$floor(x/2), ceil(x/2), abs(x), norm(x)$ diff --git a/tests/typ/math/frac.typ b/tests/typ/math/frac.typ new file mode 100644 index 000000000..27c2ae45b --- /dev/null +++ b/tests/typ/math/frac.typ @@ -0,0 +1,25 @@ +// Test fractions. + +--- +// Test that denominator baseline matches in the common case. +$ x = 1/2 = a/(a h) = a/a = a/(1/2) $ + +--- +// Test parenthesis removal. +$ (|x| + |y|)/2 < [1+2]/3 $ + +--- +// Test associativity. +$ 1/2/3 = (1/2)/3 = 1/(2/3) $ + +--- +// Test large fraction. +$ x = (-b plus.minus sqrt(b^2 - 4a c))/(2a) $ + +--- +// Test binomial. +$ binom(circle, square) $ + +--- +// Error: 8-13 missing argument: lower index +$ binom(x^2) $ diff --git a/tests/typ/math/matrix.typ b/tests/typ/math/matrix.typ index 3f65a6838..99e8fa19d 100644 --- a/tests/typ/math/matrix.typ +++ b/tests/typ/math/matrix.typ @@ -1,27 +1,26 @@ -// Test vectors, matrices, and cases. +// Test matrices. --- -$ v = vec(1, 2+3, 4) $ +// Test semicolon syntax. +#set align(center) +$mat() dot + mat(;) dot + mat(1, 2) dot + mat(1, 2;) \ + mat(1; 2) dot + mat(1, 2; 3, 4) dot + mat(1 + &2, 1/2; &3, 4)$ --- -$ binom(n, 1) = 1/2 n (n-1) $ +// Test sparse matrix. +$mat( + 1, 2, ..., 10; + 2, 2, ..., 10; + dots.v, dots.v, dots.down, dots.v; + 10, 10, ..., 10; +)$ --- -#set math.vec(delim: "|") -$ vec(1, 2) $ - ---- -$ f(x, y) := cases( - 1 "if" (x dot y)/2 <= 0, - 2 "if" x in NN, - 3 "if" x "is even", - 4 "else", -) $ - ---- -// Error: 22-25 expected "(", "[", "{", "|", or "||" -#set math.vec(delim: "%") - ---- -// Error: 8-13 missing argument: lower index -$ binom(x^2) $ +// Test alternative delimiter. +#set math.mat(delim: "[") +$ mat(1, 2; 3, 4) $ diff --git a/tests/typ/math/multiline.typ b/tests/typ/math/multiline.typ new file mode 100644 index 000000000..b57d91666 --- /dev/null +++ b/tests/typ/math/multiline.typ @@ -0,0 +1,35 @@ +// Test multiline math. + +--- +// Test basic alignment. +$ x &= x + y \ + &= x + 2z \ + &= sum x dot 2z $ + +--- +// Test text before first alignment point. +$ x + 1 &= a^2 + b^2 \ + y &= a + b^2 \ + z &= alpha dot beta $ + +--- +// Test space between inner alignment points. +$ a + b &= 2 + 3 &= 5 \ + b &= c &= 3 $ + +--- +// Test in case distinction. +$ f := cases( + 1 + 2 &"iff" &x, + 3 &"if" &y, +) $ + +--- +// Test mixing lines with and some without alignment points. +$ "abc" &= c \ + &= d + 1 \ + = x $ + +--- +// Test multiline subscript. +$ sum_(n in NN \ n <= 5) n = (5(5+1))/2 = 15 $ diff --git a/tests/typ/math/op.typ b/tests/typ/math/op.typ new file mode 100644 index 000000000..c7b34c54a --- /dev/null +++ b/tests/typ/math/op.typ @@ -0,0 +1,21 @@ +// Test text operators. + +--- +// Test predefined. +$ max_(1<=n<=m) n $ + +--- +// With or without parens. +$ &sin x + log_2 x \ + = &sin(x) + log_2(x) $ + +--- +// Test scripts vs limits. +#set text("Latin Modern Roman") +Discuss $lim_(n->infty) 1/n$ now. +$ lim_(n->infty) 1/n = 0 $ + +--- +// Test custom operator. +$ op("myop", limits: #false)_(x:=1) x \ + op("myop", limits: #true)_(x:=1) x $ diff --git a/tests/typ/math/root.typ b/tests/typ/math/root.typ new file mode 100644 index 000000000..8d3e2de02 --- /dev/null +++ b/tests/typ/math/root.typ @@ -0,0 +1,38 @@ +// Test roots. + +--- +// Test root with more than one character. +$A = sqrt(x + y) = c$ + +--- +// Test root size with radicals containing attachments. +$ sqrt(a) quad + sqrt(f) quad + sqrt(q) quad + sqrt(a^2) \ + sqrt(n_0) quad + sqrt(b^()) quad + sqrt(b^2) quad + sqrt(q_1^2) $ + +--- +// Test precomposed vs constructed roots. +// 3 and 4 are precomposed. +$sqrt(x)$ +$root(2, x)$ +$root(3, x)$ +$root(4, x)$ +$root(5, x)$ + +--- +// Test large bodies +$ sqrt([|x|]^2 + [|y|]^2) < [|z|] $ +$ v = sqrt((1/2) / (4/5)) + = root(3, (1/2/3) / (4/5/6)) + = root(4, ((1/2) / (3/4)) / ((1/2) / (3/4))) $ + +--- +// Test large index. +$ root(2, x) quad + root(3/(2/1), x) quad + root(1/11, x) $ diff --git a/tests/typ/math/shorthand.typ b/tests/typ/math/shorthand.typ deleted file mode 100644 index d37c938f0..000000000 --- a/tests/typ/math/shorthand.typ +++ /dev/null @@ -1,4 +0,0 @@ -// Test math shorthands. - ---- -$ f : NN <=> RR, n |-> sqrt(n) $ diff --git a/tests/typ/math/simple.typ b/tests/typ/math/simple.typ deleted file mode 100644 index 1b63cbfc9..000000000 --- a/tests/typ/math/simple.typ +++ /dev/null @@ -1,24 +0,0 @@ -// Test math formulas. - ---- -The sum of $a$ and $b$ is $a + b$. - ---- -We will show that: -$ a^2 + b^2 = c^2 $ - ---- -Prove by induction: -$ sum_(k=0)^n k = (n(n+1))/2 $ - ---- -We know that: -$ floor(x/2) <= ceil(x/2) $ - ---- -// Test that blackboard style looks nice. -$ f: NN -> RR $ - ---- -// Error: 1:3 expected dollar sign -$a diff --git a/tests/typ/math/spacing.typ b/tests/typ/math/spacing.typ new file mode 100644 index 000000000..6f2d6b443 --- /dev/null +++ b/tests/typ/math/spacing.typ @@ -0,0 +1,31 @@ +// Test spacing in math formulas. + +--- +// Test spacing cases. +$ä, +, c, (, )$ \ +$=), (+), {times}$ +$⟧<⟦, |-|, [=$ \ +$a=b, a==b$ \ +$-a, +a$ \ +$a not b$ \ +$a+b, a*b$ \ +$sum x, sum(x)$ \ +$sum prod x$ \ +$f(x), zeta(x), "frac"(x)$ + +--- +// Test ignored vs non-ignored spaces. +$f (x), f(x)$ \ +$[a|b], [a | b]$ \ +$a"is"b, a "is" b$ + +--- +// Test predefined spacings. +$a thin b, a med b, a thick b, a quad b$ \ +$a = thin b$ \ +$a - b ident c quad (mod 2)$ + +--- +// Test spacing for set comprehension. +#set page(width: auto) +$ { x in RR | x "is natural" and x < 10 } $ diff --git a/tests/typ/math/style.typ b/tests/typ/math/style.typ index 38d6e2d5d..228519001 100644 --- a/tests/typ/math/style.typ +++ b/tests/typ/math/style.typ @@ -1,15 +1,30 @@ -#let part = $ a B pi Delta $ -#let kinds = (math.serif, math.sans, math.cal, math.frak, math.mono, math.bb) -#let modifiers = (v => v, math.italic, math.bold, v => math.italic(math.bold(v))) +// Test text styling in math. -#let cells = (sym.triangle.nested, [--], [`italic`], [`bold`], [both]) -#for kk in kinds { - cells.push(raw(repr(kk).trim(""))) - for mm in modifiers { - cells.push($ mm(kk(part)) $) - } -} +--- +// Test italic defaults. +$a, A, delta, ϵ, diff, Delta, ϴ$ -#set page(width: auto) -#set align(center) -#table(columns: 1 + modifiers.len(), ..cells) +--- +// Test forcing a specific style. +$A, italic(A), upright(A), bold(A), bold(upright(A)), \ + serif(A), sans(A), cal(A), frak(A), mono(A), bb(A), \ + italic(diff), upright(diff), \ + bb("hello") + bold(cal("world")), \ + mono("SQRT")(x) wreath mono(123 + 456)$ + +--- +// Test a few style exceptions. +$h, bb(N), frak(R), Theta, italic(Theta), sans(Theta), sans(italic(Theta))$ + +--- +// Test font fallback. +$ よ and 🏳️‍🌈 $ + +--- +// Test text properties. +$text(#red, "time"^2) + sqrt("place")$ + +--- +// Test different font. +#show math.formula: set text(family: "Fira Math") +$ v := vec(1 + 2, 2 - 4, sqrt(3), arrow(x)) + 1 $ diff --git a/tests/typ/math/syntax.typ b/tests/typ/math/syntax.typ index 8ffded298..26d3e4449 100644 --- a/tests/typ/math/syntax.typ +++ b/tests/typ/math/syntax.typ @@ -1,23 +1,18 @@ -#set page(width: auto) -#show : it => table( - columns: 2, - inset: 8pt, - ..it.text - .split("\n") - .map(line => (raw(line, lang: "typ"), text("Latin Modern Roman", eval(line)))) - .flatten() -) +// Test math syntax. -``` -Let $x in NN$ be ... -$ (1 + x/2)^2 $ -$ x arrow.l y $ -$ sum_(n=1)^mu 1 + (2pi(5 + n)) / k $ -$ { x in RR | x "is natural" and x < 10 } $ -$ sqrt(x^2) = frac(x, 1) $ -$ "profit" = "income" - "expenses" $ -$ x < #for i in range(5) [$ #i < $] y $ -$ 1 + 2 = #{1 + 2} $ -$ A subset.eq.not B $ -``` -
+--- +// Test Unicode math. +$ ∑_(i=0)^ℕ a ∘ b = \u{2211}_(i=0)^NN a compose b $ + +--- +// Test a few shorthands. +$ underline(f' : NN -> RR) \ + n |-> cases( + [|1|] &"if" n >>> 10, + 2 * 3 &"if" n != 5, + 1 - 0 thick &..., + ) $ + +--- +// Error: 1:3 expected dollar sign +$a diff --git a/tests/typ/math/underover.typ b/tests/typ/math/underover.typ new file mode 100644 index 000000000..f0f673083 --- /dev/null +++ b/tests/typ/math/underover.typ @@ -0,0 +1,21 @@ +// Test under/over things. + +--- +// Test braces. +$ x = underbrace( + 1 + 2 + ... + 5, + underbrace("numbers", x + y) +) $ + +--- +// Test lines and brackets. +$ x = overbracket( + overline(underline(x + y)), + 1 + 2 + ... + 5, +) $ + +--- +// Test brackets. +$ underbracket([1, 2/3], "relevant stuff") + arrow.l.r.double.long + overbracket([4/5,6], "irrelevant stuff") $ diff --git a/tests/typ/math/vec.typ b/tests/typ/math/vec.typ new file mode 100644 index 000000000..198a1d154 --- /dev/null +++ b/tests/typ/math/vec.typ @@ -0,0 +1,14 @@ +// Test vectors. + +--- +// Test wide cell. +$ v = vec(1, 2+3, 4) $ + +--- +// Test alternative delimiter. +#set math.vec(delim: "[") +$ vec(1, 2) $ + +--- +// Error: 22-25 expected "(", "[", "{", "|", or "||" +#set math.vec(delim: "%") diff --git a/tools/test-helper/extension.js b/tools/test-helper/extension.js index 335fcc150..a745916a5 100644 --- a/tools/test-helper/extension.js +++ b/tools/test-helper/extension.js @@ -108,6 +108,12 @@ function getWebviewContent(pngSrc, refSrc, stdout, stderr) { } .flex { display: flex; + flex-wrap: wrap; + } + .flex > * { + flex-grow: 1; + flex-shrink: 0; + max-width: 100%; }