mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Math delimiter grouping
This commit is contained in:
parent
953bdc1859
commit
7e295d84b5
@ -189,6 +189,7 @@ fn items() -> LangItems {
|
|||||||
},
|
},
|
||||||
math: |body, block| math::MathNode { body, block }.pack(),
|
math: |body, block| math::MathNode { body, block }.pack(),
|
||||||
math_atom: |atom| math::AtomNode(atom).pack(),
|
math_atom: |atom| math::AtomNode(atom).pack(),
|
||||||
|
math_delimited: |body| math::LrNode(body).pack(),
|
||||||
math_script: |base, sub, sup| math::ScriptNode { base, sub, sup }.pack(),
|
math_script: |base, sub, sup| math::ScriptNode { base, sub, sup }.pack(),
|
||||||
math_frac: |num, denom| math::FracNode { num, denom }.pack(),
|
math_frac: |num, denom| math::FracNode { num, denom }.pack(),
|
||||||
math_align_point: || math::AlignPointNode.pack(),
|
math_align_point: || math::AlignPointNode.pack(),
|
||||||
|
78
library/src/math/lr.rs
Normal file
78
library/src/math/lr.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// How much less high scaled delimiters can be than what they wrap.
|
||||||
|
const DELIM_SHORT_FALL: Em = Em::new(0.1);
|
||||||
|
|
||||||
|
/// # Left-Right
|
||||||
|
/// Scales delimiters.
|
||||||
|
///
|
||||||
|
/// While matched delimiters scale by default, this can be used to scale
|
||||||
|
/// unmatched delimiters and to control the delimiter scaling more precisely.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ lr(]a, b/2]) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - body: Content (positional, variadic)
|
||||||
|
/// The delimited content, including the delimiters.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(LayoutMath)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct LrNode(pub Content);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl LrNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
let mut body = Content::empty();
|
||||||
|
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
body += AtomNode(','.into()).pack();
|
||||||
|
}
|
||||||
|
body += arg;
|
||||||
|
}
|
||||||
|
Ok(Self(body).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutMath for LrNode {
|
||||||
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
|
let mut row = ctx.layout_row(&self.0)?;
|
||||||
|
|
||||||
|
let axis = scaled!(ctx, axis_height);
|
||||||
|
let max_extent = row
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|fragment| (fragment.ascent() - axis).max(fragment.descent() + axis))
|
||||||
|
.max()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let height = 2.0 * max_extent;
|
||||||
|
if let [first, .., last] = row.0.as_mut_slice() {
|
||||||
|
for fragment in [first, last] {
|
||||||
|
if !matches!(
|
||||||
|
fragment.class(),
|
||||||
|
Some(MathClass::Opening | MathClass::Closing | MathClass::Fence)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let MathFragment::Glyph(glyph) = *fragment else { continue };
|
||||||
|
let short_fall = DELIM_SHORT_FALL.at(glyph.font_size);
|
||||||
|
*fragment = MathFragment::Variant(
|
||||||
|
glyph.stretch_vertical(ctx, height, short_fall),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for fragment in row.0 {
|
||||||
|
ctx.push(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ mod atom;
|
|||||||
mod braced;
|
mod braced;
|
||||||
mod frac;
|
mod frac;
|
||||||
mod fragment;
|
mod fragment;
|
||||||
|
mod lr;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
mod op;
|
mod op;
|
||||||
mod root;
|
mod root;
|
||||||
@ -50,6 +51,7 @@ use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode, Symbol
|
|||||||
/// Hook up all math definitions.
|
/// Hook up all math definitions.
|
||||||
pub fn define(scope: &mut Scope) {
|
pub fn define(scope: &mut Scope) {
|
||||||
scope.def_func::<MathNode>("math");
|
scope.def_func::<MathNode>("math");
|
||||||
|
scope.def_func::<LrNode>("lr");
|
||||||
scope.def_func::<AccentNode>("accent");
|
scope.def_func::<AccentNode>("accent");
|
||||||
scope.def_func::<FracNode>("frac");
|
scope.def_func::<FracNode>("frac");
|
||||||
scope.def_func::<BinomNode>("binom");
|
scope.def_func::<BinomNode>("binom");
|
||||||
|
@ -115,6 +115,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
|
|||||||
SyntaxKind::TermMarker => Some(Category::ListMarker),
|
SyntaxKind::TermMarker => Some(Category::ListMarker),
|
||||||
SyntaxKind::Math => None,
|
SyntaxKind::Math => None,
|
||||||
SyntaxKind::Atom => None,
|
SyntaxKind::Atom => None,
|
||||||
|
SyntaxKind::Delimited => None,
|
||||||
SyntaxKind::Script => None,
|
SyntaxKind::Script => None,
|
||||||
SyntaxKind::Frac => None,
|
SyntaxKind::Frac => None,
|
||||||
SyntaxKind::AlignPoint => Some(Category::MathOperator),
|
SyntaxKind::AlignPoint => Some(Category::MathOperator),
|
||||||
|
@ -280,6 +280,7 @@ impl Eval for ast::Expr {
|
|||||||
Self::Enum(v) => v.eval(vm).map(Value::Content),
|
Self::Enum(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Term(v) => v.eval(vm).map(Value::Content),
|
Self::Term(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Atom(v) => v.eval(vm).map(Value::Content),
|
Self::Atom(v) => v.eval(vm).map(Value::Content),
|
||||||
|
Self::Delimited(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Script(v) => v.eval(vm).map(Value::Content),
|
Self::Script(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Frac(v) => v.eval(vm).map(Value::Content),
|
Self::Frac(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::AlignPoint(v) => v.eval(vm).map(Value::Content),
|
Self::AlignPoint(v) => v.eval(vm).map(Value::Content),
|
||||||
@ -325,10 +326,19 @@ impl ast::Expr {
|
|||||||
Self::Shorthand(v) => v.eval_in_math(vm)?,
|
Self::Shorthand(v) => v.eval_in_math(vm)?,
|
||||||
Self::Symbol(v) => v.eval_in_math(vm)?,
|
Self::Symbol(v) => v.eval_in_math(vm)?,
|
||||||
Self::Ident(v) => v.eval_in_math(vm)?,
|
Self::Ident(v) => v.eval_in_math(vm)?,
|
||||||
|
Self::FuncCall(v) => v.eval_in_math(vm)?,
|
||||||
_ => self.eval(vm)?.display_in_math(),
|
_ => self.eval(vm)?.display_in_math(),
|
||||||
}
|
}
|
||||||
.spanned(self.span()))
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_without_parens(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||||
|
Ok(match self {
|
||||||
|
Self::Delimited(v) => v.eval_without_parens(vm)?,
|
||||||
|
_ => self.eval_in_math(vm)?,
|
||||||
|
}
|
||||||
|
.spanned(self.span()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::Text {
|
impl Eval for ast::Text {
|
||||||
@ -401,7 +411,7 @@ impl Eval for ast::Symbol {
|
|||||||
|
|
||||||
impl ast::Symbol {
|
impl ast::Symbol {
|
||||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||||
Ok((vm.items.symbol)(EcoString::from(self.get()) + ":op".into()))
|
Ok((vm.items.symbol)(EcoString::from(self.get()) + ":op:square".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,7 +521,7 @@ impl Eval for ast::Math {
|
|||||||
.map(|expr| expr.eval_in_math(vm))
|
.map(|expr| expr.eval_in_math(vm))
|
||||||
.collect::<SourceResult<_>>()?;
|
.collect::<SourceResult<_>>()?;
|
||||||
let block = self.block();
|
let block = self.block();
|
||||||
Ok((vm.items.math)(seq, block))
|
Ok((vm.items.math)(Content::sequence(seq), block))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,13 +533,44 @@ impl Eval for ast::Atom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Delimited {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
let seq = self
|
||||||
|
.exprs()
|
||||||
|
.map(|expr| expr.eval_in_math(vm))
|
||||||
|
.collect::<SourceResult<_>>()?;
|
||||||
|
Ok((vm.items.math_delimited)(Content::sequence(seq)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ast::Delimited {
|
||||||
|
fn eval_without_parens(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||||
|
let exprs: Vec<_> = self.exprs().collect();
|
||||||
|
let mut slice = exprs.as_slice();
|
||||||
|
if let (Some(ast::Expr::Atom(first)), Some(ast::Expr::Atom(last))) =
|
||||||
|
(exprs.first(), exprs.last())
|
||||||
|
{
|
||||||
|
if first.get() == "(" && last.get() == ")" {
|
||||||
|
slice = &exprs[1..exprs.len() - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let seq = slice
|
||||||
|
.iter()
|
||||||
|
.map(|expr| expr.eval_in_math(vm))
|
||||||
|
.collect::<SourceResult<_>>()?;
|
||||||
|
Ok((vm.items.math_delimited)(Content::sequence(seq)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eval for ast::Script {
|
impl Eval for ast::Script {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let base = self.base().eval_in_math(vm)?;
|
let base = self.base().eval_in_math(vm)?;
|
||||||
let sub = self.sub().map(|expr| expr.eval_in_math(vm)).transpose()?;
|
let sub = self.sub().map(|expr| expr.eval_without_parens(vm)).transpose()?;
|
||||||
let sup = self.sup().map(|expr| expr.eval_in_math(vm)).transpose()?;
|
let sup = self.sup().map(|expr| expr.eval_without_parens(vm)).transpose()?;
|
||||||
Ok((vm.items.math_script)(base, sub, sup))
|
Ok((vm.items.math_script)(base, sub, sup))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,8 +579,8 @@ impl Eval for ast::Frac {
|
|||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let num = self.num().eval_in_math(vm)?;
|
let num = self.num().eval_without_parens(vm)?;
|
||||||
let denom = self.denom().eval_in_math(vm)?;
|
let denom = self.denom().eval_without_parens(vm)?;
|
||||||
Ok((vm.items.math_frac)(num, denom))
|
Ok((vm.items.math_frac)(num, denom))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -878,12 +919,29 @@ impl Eval for ast::FuncCall {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
let callee = self.callee();
|
||||||
|
let callee = callee.eval(vm)?.cast::<Func>().at(callee.span())?;
|
||||||
|
self.eval_with_callee(vm, callee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ast::FuncCall {
|
||||||
|
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||||
|
let callee = self.callee().eval(vm)?;
|
||||||
|
if let Value::Func(callee) = callee {
|
||||||
|
Ok(self.eval_with_callee(vm, callee)?.display_in_math())
|
||||||
|
} else {
|
||||||
|
Ok(callee.display_in_math()
|
||||||
|
+ (vm.items.math_atom)("(".into())
|
||||||
|
+ (vm.items.math_atom)(")".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_with_callee(&self, vm: &mut Vm, callee: Func) -> SourceResult<Value> {
|
||||||
if vm.depth >= MAX_CALL_DEPTH {
|
if vm.depth >= MAX_CALL_DEPTH {
|
||||||
bail!(self.span(), "maximum function call depth exceeded");
|
bail!(self.span(), "maximum function call depth exceeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
let callee = self.callee();
|
|
||||||
let callee = callee.eval(vm)?.cast::<Func>().at(callee.span())?;
|
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
let point = || Tracepoint::Call(callee.name().map(Into::into));
|
let point = || Tracepoint::Call(callee.name().map(Into::into));
|
||||||
callee.call(vm, args).trace(vm.world, point, self.span())
|
callee.call(vm, args).trace(vm.world, point, self.span())
|
||||||
|
@ -66,7 +66,10 @@ pub struct LangItems {
|
|||||||
/// An item in a term list: `/ Term: Details`.
|
/// An item in a term list: `/ Term: Details`.
|
||||||
pub term_item: fn(term: Content, description: Content) -> Content,
|
pub term_item: fn(term: Content, description: Content) -> Content,
|
||||||
/// A mathematical formula: `$x$`, `$ x^2 $`.
|
/// A mathematical formula: `$x$`, `$ x^2 $`.
|
||||||
pub math: fn(children: Vec<Content>, block: bool) -> Content,
|
pub math: fn(body: Content, block: bool) -> Content,
|
||||||
|
/// A subsection in a math formula that is surrounded by matched delimiters:
|
||||||
|
/// `[x + y]`.
|
||||||
|
pub math_delimited: fn(body: Content) -> Content,
|
||||||
/// An atom in a formula: `x`, `+`, `12`.
|
/// An atom in a formula: `x`, `+`, `12`.
|
||||||
pub math_atom: fn(atom: EcoString) -> Content,
|
pub math_atom: fn(atom: EcoString) -> Content,
|
||||||
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
|
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
|
||||||
|
@ -117,6 +117,9 @@ pub enum Expr {
|
|||||||
Math(Math),
|
Math(Math),
|
||||||
/// An atom in a math formula: `x`, `+`, `12`.
|
/// An atom in a math formula: `x`, `+`, `12`.
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
|
/// A subsection in a math formula that is surrounded by matched delimiters:
|
||||||
|
/// `[x + y]`.
|
||||||
|
Delimited(Delimited),
|
||||||
/// A base with optional sub- and superscripts in a math formula: `a_1^2`.
|
/// A base with optional sub- and superscripts in a math formula: `a_1^2`.
|
||||||
Script(Script),
|
Script(Script),
|
||||||
/// A fraction in a math formula: `x/2`.
|
/// A fraction in a math formula: `x/2`.
|
||||||
@ -216,6 +219,7 @@ impl AstNode for Expr {
|
|||||||
SyntaxKind::TermItem => node.cast().map(Self::Term),
|
SyntaxKind::TermItem => node.cast().map(Self::Term),
|
||||||
SyntaxKind::Math => node.cast().map(Self::Math),
|
SyntaxKind::Math => node.cast().map(Self::Math),
|
||||||
SyntaxKind::Atom => node.cast().map(Self::Atom),
|
SyntaxKind::Atom => node.cast().map(Self::Atom),
|
||||||
|
SyntaxKind::Delimited => node.cast().map(Self::Delimited),
|
||||||
SyntaxKind::Script => node.cast().map(Self::Script),
|
SyntaxKind::Script => node.cast().map(Self::Script),
|
||||||
SyntaxKind::Frac => node.cast().map(Self::Frac),
|
SyntaxKind::Frac => node.cast().map(Self::Frac),
|
||||||
SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
|
SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
|
||||||
@ -275,6 +279,7 @@ impl AstNode for Expr {
|
|||||||
Self::Term(v) => v.as_untyped(),
|
Self::Term(v) => v.as_untyped(),
|
||||||
Self::Math(v) => v.as_untyped(),
|
Self::Math(v) => v.as_untyped(),
|
||||||
Self::Atom(v) => v.as_untyped(),
|
Self::Atom(v) => v.as_untyped(),
|
||||||
|
Self::Delimited(v) => v.as_untyped(),
|
||||||
Self::Script(v) => v.as_untyped(),
|
Self::Script(v) => v.as_untyped(),
|
||||||
Self::Frac(v) => v.as_untyped(),
|
Self::Frac(v) => v.as_untyped(),
|
||||||
Self::AlignPoint(v) => v.as_untyped(),
|
Self::AlignPoint(v) => v.as_untyped(),
|
||||||
@ -657,6 +662,19 @@ impl Atom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// A subsection in a math formula that is surrounded by matched delimiters:
|
||||||
|
/// `[x + y]`.
|
||||||
|
Delimited
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Delimited {
|
||||||
|
/// The contents, including the delimiters.
|
||||||
|
pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
|
||||||
|
self.0.children().filter_map(Expr::cast_with_space)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A base with an optional sub- and superscript in a formula: `a_1^2`.
|
/// A base with an optional sub- and superscript in a formula: `a_1^2`.
|
||||||
Script
|
Script
|
||||||
@ -673,8 +691,7 @@ impl Script {
|
|||||||
self.0
|
self.0
|
||||||
.children()
|
.children()
|
||||||
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
|
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
|
||||||
.nth(1)
|
.find_map(SyntaxNode::cast)
|
||||||
.map(|node| node.cast().expect("script node has invalid subscript"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The superscript.
|
/// The superscript.
|
||||||
@ -682,8 +699,7 @@ impl Script {
|
|||||||
self.0
|
self.0
|
||||||
.children()
|
.children()
|
||||||
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
|
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
|
||||||
.nth(1)
|
.find_map(SyntaxNode::cast)
|
||||||
.map(|node| node.cast().expect("script node has invalid superscript"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,9 @@ pub enum SyntaxKind {
|
|||||||
Math,
|
Math,
|
||||||
/// An atom in math: `x`, `+`, `12`.
|
/// An atom in math: `x`, `+`, `12`.
|
||||||
Atom,
|
Atom,
|
||||||
|
/// A subsection in a math formula that is surrounded by matched delimiters:
|
||||||
|
/// `[x + y]`.
|
||||||
|
Delimited,
|
||||||
/// A base with optional sub- and superscripts in math: `a_1^2`.
|
/// A base with optional sub- and superscripts in math: `a_1^2`.
|
||||||
Script,
|
Script,
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
@ -336,6 +339,7 @@ impl SyntaxKind {
|
|||||||
Self::TermItem => "term list item",
|
Self::TermItem => "term list item",
|
||||||
Self::TermMarker => "term marker",
|
Self::TermMarker => "term marker",
|
||||||
Self::Math => "math formula",
|
Self::Math => "math formula",
|
||||||
|
Self::Delimited => "delimited math",
|
||||||
Self::Atom => "math atom",
|
Self::Atom => "math atom",
|
||||||
Self::Script => "script",
|
Self::Script => "script",
|
||||||
Self::Frac => "fraction",
|
Self::Frac => "fraction",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use super::{ast, is_newline, ErrorPos, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
use super::{ast, is_newline, ErrorPos, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
||||||
use crate::util::{format_eco, EcoString};
|
use crate::util::{format_eco, EcoString};
|
||||||
|
|
||||||
@ -233,12 +235,13 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Atom => match p.current_text() {
|
SyntaxKind::Atom if math_class(p.current_text()) == Some(MathClass::Fence) => {
|
||||||
"(" => math_delimited(p, ")"),
|
math_delimited(p, MathClass::Fence)
|
||||||
"{" => math_delimited(p, "}"),
|
}
|
||||||
"[" => math_delimited(p, "]"),
|
|
||||||
_ => p.eat(),
|
SyntaxKind::Atom if math_class(p.current_text()) == Some(MathClass::Opening) => {
|
||||||
},
|
math_delimited(p, MathClass::Closing)
|
||||||
|
}
|
||||||
|
|
||||||
SyntaxKind::Let
|
SyntaxKind::Let
|
||||||
| SyntaxKind::Set
|
| SyntaxKind::Set
|
||||||
@ -254,7 +257,8 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
| SyntaxKind::LeftBrace
|
| SyntaxKind::LeftBrace
|
||||||
| SyntaxKind::LeftBracket => embedded_code_expr(p),
|
| SyntaxKind::LeftBracket => embedded_code_expr(p),
|
||||||
|
|
||||||
SyntaxKind::Linebreak
|
SyntaxKind::Atom
|
||||||
|
| SyntaxKind::Linebreak
|
||||||
| SyntaxKind::Escape
|
| SyntaxKind::Escape
|
||||||
| SyntaxKind::Shorthand
|
| SyntaxKind::Shorthand
|
||||||
| SyntaxKind::Symbol
|
| SyntaxKind::Symbol
|
||||||
@ -288,21 +292,30 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn math_delimited(p: &mut Parser, closing: &str) {
|
fn math_delimited(p: &mut Parser, stop: MathClass) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
p.expect(SyntaxKind::Atom);
|
p.expect(SyntaxKind::Atom);
|
||||||
while !p.eof()
|
while !p.eof() && !p.at(SyntaxKind::Dollar) {
|
||||||
&& !p.at(SyntaxKind::Dollar)
|
if math_class(p.current_text()) == Some(stop) {
|
||||||
&& (!p.at(SyntaxKind::Atom) || p.current_text() != closing)
|
p.eat();
|
||||||
{
|
p.wrap(m, SyntaxKind::Delimited);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let prev = p.prev_end();
|
let prev = p.prev_end();
|
||||||
math_expr(p);
|
math_expr(p);
|
||||||
if !p.progress(prev) {
|
if !p.progress(prev) {
|
||||||
p.unexpected();
|
p.unexpected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.expect(SyntaxKind::Atom);
|
}
|
||||||
p.wrap(m, SyntaxKind::Math);
|
|
||||||
|
fn math_class(text: &str) -> Option<MathClass> {
|
||||||
|
let mut chars = text.chars();
|
||||||
|
chars
|
||||||
|
.next()
|
||||||
|
.filter(|_| chars.next().is_none())
|
||||||
|
.and_then(unicode_math_class::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usize)> {
|
fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usize)> {
|
||||||
@ -324,7 +337,7 @@ fn math_args(p: &mut Parser) {
|
|||||||
p.expect(SyntaxKind::Atom);
|
p.expect(SyntaxKind::Atom);
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
let mut m2 = p.marker();
|
let mut m2 = p.marker();
|
||||||
while !p.eof() {
|
while !p.eof() && !p.at(SyntaxKind::Dollar) {
|
||||||
match p.current_text() {
|
match p.current_text() {
|
||||||
")" => break,
|
")" => break,
|
||||||
"," => {
|
"," => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user