mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
Allow somewhat arbitrary characters as mat
, vec
and cases
delim
(#4211)
This commit is contained in:
parent
babf641c3a
commit
a6cf584ee9
@ -1,9 +1,10 @@
|
|||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use crate::diag::{bail, At, SourceResult, StrResult};
|
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, dict, elem, Array, Cast, Content, Dict, Fold, Packed, Resolve, Smart,
|
array, cast, dict, elem, Array, Content, Dict, Fold, NoneValue, Packed, Resolve,
|
||||||
StyleChain, Value,
|
Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Length, Point, Ratio, Rel, Size,
|
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Length, Point, Ratio, Rel, Size,
|
||||||
@ -13,6 +14,7 @@ use crate::math::{
|
|||||||
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled,
|
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled,
|
||||||
DELIM_SHORT_FALL,
|
DELIM_SHORT_FALL,
|
||||||
};
|
};
|
||||||
|
use crate::symbols::Symbol;
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
use crate::utils::Numeric;
|
use crate::utils::Numeric;
|
||||||
@ -40,8 +42,8 @@ pub struct VecElem {
|
|||||||
/// #set math.vec(delim: "[")
|
/// #set math.vec(delim: "[")
|
||||||
/// $ vec(1, 2) $
|
/// $ vec(1, 2) $
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Some(Delimiter::Paren))]
|
#[default(DelimiterPair::PAREN)]
|
||||||
pub delim: Option<Delimiter>,
|
pub delim: DelimiterPair,
|
||||||
|
|
||||||
/// The gap between elements.
|
/// The gap between elements.
|
||||||
///
|
///
|
||||||
@ -71,14 +73,7 @@ impl LayoutMath for Packed<VecElem> {
|
|||||||
LeftRightAlternator::Right,
|
LeftRightAlternator::Right,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
layout_delimiters(
|
layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), self.span())
|
||||||
ctx,
|
|
||||||
styles,
|
|
||||||
frame,
|
|
||||||
delim.map(Delimiter::open),
|
|
||||||
delim.map(Delimiter::close),
|
|
||||||
self.span(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +104,8 @@ pub struct MatElem {
|
|||||||
/// #set math.mat(delim: "[")
|
/// #set math.mat(delim: "[")
|
||||||
/// $ mat(1, 2; 3, 4) $
|
/// $ mat(1, 2; 3, 4) $
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Some(Delimiter::Paren))]
|
#[default(DelimiterPair::PAREN)]
|
||||||
pub delim: Option<Delimiter>,
|
pub delim: DelimiterPair,
|
||||||
|
|
||||||
/// Draws augmentation lines in a matrix.
|
/// Draws augmentation lines in a matrix.
|
||||||
///
|
///
|
||||||
@ -257,14 +252,7 @@ impl LayoutMath for Packed<MatElem> {
|
|||||||
self.span(),
|
self.span(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
layout_delimiters(
|
layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), self.span())
|
||||||
ctx,
|
|
||||||
styles,
|
|
||||||
frame,
|
|
||||||
delim.map(Delimiter::open),
|
|
||||||
delim.map(Delimiter::close),
|
|
||||||
self.span(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,8 +277,8 @@ pub struct CasesElem {
|
|||||||
/// #set math.cases(delim: "[")
|
/// #set math.cases(delim: "[")
|
||||||
/// $ x = cases(1, 2) $
|
/// $ x = cases(1, 2) $
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Delimiter::Brace)]
|
#[default(DelimiterPair::BRACE)]
|
||||||
pub delim: Delimiter,
|
pub delim: DelimiterPair,
|
||||||
|
|
||||||
/// Whether the direction of cases should be reversed.
|
/// Whether the direction of cases should be reversed.
|
||||||
///
|
///
|
||||||
@ -330,56 +318,108 @@ impl LayoutMath for Packed<CasesElem> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (open, close) = if self.reverse(styles) {
|
let (open, close) = if self.reverse(styles) {
|
||||||
(None, Some(delim.close()))
|
(None, delim.close())
|
||||||
} else {
|
} else {
|
||||||
(Some(delim.open()), None)
|
(delim.open(), None)
|
||||||
};
|
};
|
||||||
|
|
||||||
layout_delimiters(ctx, styles, frame, open, close, self.span())
|
layout_delimiters(ctx, styles, frame, open, close, self.span())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A vector / matrix delimiter.
|
/// A delimiter is a single character that is used to delimit a matrix, vector
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
/// or cases. The character has to be a Unicode codepoint tagged as a math
|
||||||
pub enum Delimiter {
|
/// "opening", "closing" or "fence".
|
||||||
/// Delimit with parentheses.
|
///
|
||||||
#[string("(")]
|
/// Typically, the delimiter is stretched to fit the height of whatever it
|
||||||
Paren,
|
/// delimits.
|
||||||
/// Delimit with brackets.
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
#[string("[")]
|
struct Delimiter(Option<char>);
|
||||||
Bracket,
|
|
||||||
/// Delimit with curly braces.
|
cast! {
|
||||||
#[string("{")]
|
Delimiter,
|
||||||
Brace,
|
self => self.0.into_value(),
|
||||||
/// Delimit with vertical bars.
|
_: NoneValue => Self::none(),
|
||||||
#[string("|")]
|
v: Symbol => Self::char(v.get())?,
|
||||||
Bar,
|
v: char => Self::char(v)?,
|
||||||
/// Delimit with double vertical bars.
|
|
||||||
#[string("||")]
|
|
||||||
DoubleBar,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Delimiter {
|
impl Delimiter {
|
||||||
/// The delimiter's opening character.
|
fn none() -> Self {
|
||||||
fn open(self) -> char {
|
Self(None)
|
||||||
match self {
|
}
|
||||||
Self::Paren => '(',
|
|
||||||
Self::Bracket => '[',
|
fn char(c: char) -> StrResult<Self> {
|
||||||
Self::Brace => '{',
|
if !matches!(
|
||||||
Self::Bar => '|',
|
unicode_math_class::class(c),
|
||||||
Self::DoubleBar => '‖',
|
Some(MathClass::Opening | MathClass::Closing | MathClass::Fence),
|
||||||
|
) {
|
||||||
|
bail!("invalid delimiter: \"{}\"", c)
|
||||||
}
|
}
|
||||||
|
Ok(Self(Some(c)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(self) -> Option<char> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching(self) -> Self {
|
||||||
|
match self.0 {
|
||||||
|
None => Self::none(),
|
||||||
|
Some('[') => Self(Some(']')),
|
||||||
|
Some(']') => Self(Some('[')),
|
||||||
|
Some('{') => Self(Some('}')),
|
||||||
|
Some('}') => Self(Some('{')),
|
||||||
|
Some(c) => match unicode_math_class::class(c) {
|
||||||
|
Some(MathClass::Opening) => Self(char::from_u32(c as u32 + 1)),
|
||||||
|
Some(MathClass::Closing) => Self(char::from_u32(c as u32 - 1)),
|
||||||
|
_ => Self(Some(c)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A pair of delimiters (one closing, one opening) used for matrices, vectors
|
||||||
|
/// and cases.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct DelimiterPair {
|
||||||
|
open: Delimiter,
|
||||||
|
close: Delimiter,
|
||||||
|
}
|
||||||
|
|
||||||
|
cast! {
|
||||||
|
DelimiterPair,
|
||||||
|
|
||||||
|
self => array![self.open, self.close].into_value(),
|
||||||
|
|
||||||
|
v: Array => match v.as_slice() {
|
||||||
|
[open, close] => Self {
|
||||||
|
open: open.clone().cast()?,
|
||||||
|
close: close.clone().cast()?,
|
||||||
|
},
|
||||||
|
_ => bail!("expected 2 delimiters, found {}", v.len())
|
||||||
|
},
|
||||||
|
v: Delimiter => Self { open: v, close: v.find_matching() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelimiterPair {
|
||||||
|
const PAREN: Self = Self {
|
||||||
|
open: Delimiter(Some('(')),
|
||||||
|
close: Delimiter(Some(')')),
|
||||||
|
};
|
||||||
|
const BRACE: Self = Self {
|
||||||
|
open: Delimiter(Some('{')),
|
||||||
|
close: Delimiter(Some('}')),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The delimiter's opening character.
|
||||||
|
fn open(self) -> Option<char> {
|
||||||
|
self.open.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The delimiter's closing character.
|
/// The delimiter's closing character.
|
||||||
fn close(self) -> char {
|
fn close(self) -> Option<char> {
|
||||||
match self {
|
self.close.get()
|
||||||
Self::Paren => ')',
|
|
||||||
Self::Bracket => ']',
|
|
||||||
Self::Brace => '}',
|
|
||||||
Self::Bar => '|',
|
|
||||||
Self::DoubleBar => '‖',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
tests/ref/math-cases-delim.png
Normal file
BIN
tests/ref/math-cases-delim.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 308 B |
BIN
tests/ref/math-mat-delims-inverted.png
Normal file
BIN
tests/ref/math-mat-delims-inverted.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/ref/math-mat-delims-pair.png
Normal file
BIN
tests/ref/math-mat-delims-pair.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 521 B |
BIN
tests/ref/math-mat-delims.png
Normal file
BIN
tests/ref/math-mat-delims.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
@ -11,3 +11,7 @@ $ f(x, y) := cases(
|
|||||||
--- math-cases-gap ---
|
--- math-cases-gap ---
|
||||||
#set math.cases(gap: 1em)
|
#set math.cases(gap: 1em)
|
||||||
$ x = cases(1, 2) $
|
$ x = cases(1, 2) $
|
||||||
|
|
||||||
|
--- math-cases-delim ---
|
||||||
|
#set math.cases(delim: sym.angle.l)
|
||||||
|
$ cases(a, b, c) $
|
||||||
|
@ -161,3 +161,54 @@ $ mat(#1, #(foo: "bar")) $
|
|||||||
)
|
)
|
||||||
|
|
||||||
$mat(augment: #1, M, v) arrow.r.squiggly mat(augment: #1, R, b)$
|
$mat(augment: #1, M, v) arrow.r.squiggly mat(augment: #1, R, b)$
|
||||||
|
|
||||||
|
--- math-mat-delims ---
|
||||||
|
$ mat(delim: #none, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "(", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \(, 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: paren.l, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "[", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \[, 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bracket.l, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "⟦", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bracket.double.l, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "{", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \{, 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: brace.l, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "|", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \|, 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bar.v, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "‖", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bar.v.double, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "⟨", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: angle.l, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
--- math-mat-delims-inverted ---
|
||||||
|
$ mat(delim: ")", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \), 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: paren.r, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "]", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \], 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bracket.r, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "⟧", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: bracket.double.r, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "}", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: \}, 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: brace.r, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
$ mat(delim: "⟩", 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: angle.r, 1, 2; 3, 4) $
|
||||||
|
|
||||||
|
--- math-mat-delims-pair ---
|
||||||
|
$ mat(delim: #(none, "["), 1, 2; 3, 4) $
|
||||||
|
$ mat(delim: #(sym.angle.r, sym.bracket.double.r), 1, 2; 3, 4) $
|
||||||
|
@ -22,6 +22,26 @@ $ v = vec(1, 2+3, 4) $
|
|||||||
#set math.vec(delim: "[")
|
#set math.vec(delim: "[")
|
||||||
$ vec(1, 2) $
|
$ vec(1, 2) $
|
||||||
|
|
||||||
--- math-vec-delim-invalid ---
|
--- math-vec-delim-empty-string ---
|
||||||
// Error: 22-25 expected "(", "[", "{", "|", "||", or none
|
// Error: 22-24 expected exactly one character
|
||||||
|
#set math.vec(delim: "")
|
||||||
|
|
||||||
|
--- math-vec-delim-not-single-char ---
|
||||||
|
// Error: 22-39 expected exactly one character
|
||||||
|
#set math.vec(delim: "not a delimiter")
|
||||||
|
|
||||||
|
--- math-vec-delim-invalid-char ---
|
||||||
|
// Error: 22-25 invalid delimiter: "%"
|
||||||
#set math.vec(delim: "%")
|
#set math.vec(delim: "%")
|
||||||
|
|
||||||
|
--- math-vec-delim-invalid-symbol ---
|
||||||
|
// Error: 22-33 invalid delimiter: "%"
|
||||||
|
#set math.vec(delim: sym.percent)
|
||||||
|
|
||||||
|
--- math-vec-delim-invalid-opening ---
|
||||||
|
// Error: 22-33 invalid delimiter: "%"
|
||||||
|
#set math.vec(delim: ("%", none))
|
||||||
|
|
||||||
|
--- math-vec-delim-invalid-closing ---
|
||||||
|
// Error: 22-33 invalid delimiter: "%"
|
||||||
|
#set math.vec(delim: (none, "%"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user