summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/math/matrix.rs160
-rw-r--r--tests/ref/math-cases-delim.pngbin0 -> 308 bytes
-rw-r--r--tests/ref/math-mat-delims-inverted.pngbin0 -> 1908 bytes
-rw-r--r--tests/ref/math-mat-delims-pair.pngbin0 -> 521 bytes
-rw-r--r--tests/ref/math-mat-delims.pngbin0 -> 2966 bytes
-rw-r--r--tests/suite/math/cases.typ4
-rw-r--r--tests/suite/math/mat.typ51
-rw-r--r--tests/suite/math/vec.typ24
8 files changed, 177 insertions, 62 deletions
diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs
index ca62846a..138a494b 100644
--- a/crates/typst/src/math/matrix.rs
+++ b/crates/typst/src/math/matrix.rs
@@ -1,9 +1,10 @@
use smallvec::{smallvec, SmallVec};
+use unicode_math_class::MathClass;
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::foundations::{
- cast, dict, elem, Array, Cast, Content, Dict, Fold, Packed, Resolve, Smart,
- StyleChain, Value,
+ array, cast, dict, elem, Array, Content, Dict, Fold, NoneValue, Packed, Resolve,
+ Smart, StyleChain, Value,
};
use crate::layout::{
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Length, Point, Ratio, Rel, Size,
@@ -13,6 +14,7 @@ use crate::math::{
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled,
DELIM_SHORT_FALL,
};
+use crate::symbols::Symbol;
use crate::syntax::{Span, Spanned};
use crate::text::TextElem;
use crate::utils::Numeric;
@@ -40,8 +42,8 @@ pub struct VecElem {
/// #set math.vec(delim: "[")
/// $ vec(1, 2) $
/// ```
- #[default(Some(Delimiter::Paren))]
- pub delim: Option<Delimiter>,
+ #[default(DelimiterPair::PAREN)]
+ pub delim: DelimiterPair,
/// The gap between elements.
///
@@ -71,14 +73,7 @@ impl LayoutMath for Packed<VecElem> {
LeftRightAlternator::Right,
)?;
- layout_delimiters(
- ctx,
- styles,
- frame,
- delim.map(Delimiter::open),
- delim.map(Delimiter::close),
- self.span(),
- )
+ layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), self.span())
}
}
@@ -109,8 +104,8 @@ pub struct MatElem {
/// #set math.mat(delim: "[")
/// $ mat(1, 2; 3, 4) $
/// ```
- #[default(Some(Delimiter::Paren))]
- pub delim: Option<Delimiter>,
+ #[default(DelimiterPair::PAREN)]
+ pub delim: DelimiterPair,
/// Draws augmentation lines in a matrix.
///
@@ -257,14 +252,7 @@ impl LayoutMath for Packed<MatElem> {
self.span(),
)?;
- layout_delimiters(
- ctx,
- styles,
- frame,
- delim.map(Delimiter::open),
- delim.map(Delimiter::close),
- self.span(),
- )
+ layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), self.span())
}
}
@@ -289,8 +277,8 @@ pub struct CasesElem {
/// #set math.cases(delim: "[")
/// $ x = cases(1, 2) $
/// ```
- #[default(Delimiter::Brace)]
- pub delim: Delimiter,
+ #[default(DelimiterPair::BRACE)]
+ pub delim: DelimiterPair,
/// Whether the direction of cases should be reversed.
///
@@ -330,59 +318,111 @@ impl LayoutMath for Packed<CasesElem> {
)?;
let (open, close) = if self.reverse(styles) {
- (None, Some(delim.close()))
+ (None, delim.close())
} else {
- (Some(delim.open()), None)
+ (delim.open(), None)
};
layout_delimiters(ctx, styles, frame, open, close, self.span())
}
}
-/// A vector / matrix delimiter.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
-pub enum Delimiter {
- /// Delimit with parentheses.
- #[string("(")]
- Paren,
- /// Delimit with brackets.
- #[string("[")]
- Bracket,
- /// Delimit with curly braces.
- #[string("{")]
- Brace,
- /// Delimit with vertical bars.
- #[string("|")]
- Bar,
- /// Delimit with double vertical bars.
- #[string("||")]
- DoubleBar,
+/// A delimiter is a single character that is used to delimit a matrix, vector
+/// or cases. The character has to be a Unicode codepoint tagged as a math
+/// "opening", "closing" or "fence".
+///
+/// Typically, the delimiter is stretched to fit the height of whatever it
+/// delimits.
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
+struct Delimiter(Option<char>);
+
+cast! {
+ Delimiter,
+ self => self.0.into_value(),
+ _: NoneValue => Self::none(),
+ v: Symbol => Self::char(v.get())?,
+ v: char => Self::char(v)?,
}
impl Delimiter {
- /// The delimiter's opening character.
- fn open(self) -> char {
- match self {
- Self::Paren => '(',
- Self::Bracket => '[',
- Self::Brace => '{',
- Self::Bar => '|',
- Self::DoubleBar => '‖',
+ fn none() -> Self {
+ Self(None)
+ }
+
+ fn char(c: char) -> StrResult<Self> {
+ if !matches!(
+ unicode_math_class::class(c),
+ Some(MathClass::Opening | MathClass::Closing | MathClass::Fence),
+ ) {
+ bail!("invalid delimiter: \"{}\"", c)
}
+ Ok(Self(Some(c)))
}
- /// The delimiter's closing character.
- fn close(self) -> char {
- match self {
- Self::Paren => ')',
- Self::Bracket => ']',
- Self::Brace => '}',
- Self::Bar => '|',
- Self::DoubleBar => '‖',
+ 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.
+ fn close(self) -> Option<char> {
+ self.close.get()
+ }
+}
+
/// Layout the inner contents of a vector.
fn layout_vec_body(
ctx: &mut MathContext,
diff --git a/tests/ref/math-cases-delim.png b/tests/ref/math-cases-delim.png
new file mode 100644
index 00000000..e54e277c
--- /dev/null
+++ b/tests/ref/math-cases-delim.png
Binary files differ
diff --git a/tests/ref/math-mat-delims-inverted.png b/tests/ref/math-mat-delims-inverted.png
new file mode 100644
index 00000000..06f1cdb3
--- /dev/null
+++ b/tests/ref/math-mat-delims-inverted.png
Binary files differ
diff --git a/tests/ref/math-mat-delims-pair.png b/tests/ref/math-mat-delims-pair.png
new file mode 100644
index 00000000..954e6d82
--- /dev/null
+++ b/tests/ref/math-mat-delims-pair.png
Binary files differ
diff --git a/tests/ref/math-mat-delims.png b/tests/ref/math-mat-delims.png
new file mode 100644
index 00000000..6ba589c8
--- /dev/null
+++ b/tests/ref/math-mat-delims.png
Binary files differ
diff --git a/tests/suite/math/cases.typ b/tests/suite/math/cases.typ
index e6c4956d..2cf48e6f 100644
--- a/tests/suite/math/cases.typ
+++ b/tests/suite/math/cases.typ
@@ -11,3 +11,7 @@ $ f(x, y) := cases(
--- math-cases-gap ---
#set math.cases(gap: 1em)
$ x = cases(1, 2) $
+
+--- math-cases-delim ---
+#set math.cases(delim: sym.angle.l)
+$ cases(a, b, c) $
diff --git a/tests/suite/math/mat.typ b/tests/suite/math/mat.typ
index e6148a34..85f918ee 100644
--- a/tests/suite/math/mat.typ
+++ b/tests/suite/math/mat.typ
@@ -161,3 +161,54 @@ $ mat(#1, #(foo: "bar")) $
)
$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) $
diff --git a/tests/suite/math/vec.typ b/tests/suite/math/vec.typ
index 312c0ee4..d7bc0b6c 100644
--- a/tests/suite/math/vec.typ
+++ b/tests/suite/math/vec.typ
@@ -22,6 +22,26 @@ $ v = vec(1, 2+3, 4) $
#set math.vec(delim: "[")
$ vec(1, 2) $
---- math-vec-delim-invalid ---
-// Error: 22-25 expected "(", "[", "{", "|", "||", or none
+--- math-vec-delim-empty-string ---
+// 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: "%")
+
+--- 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, "%"))