summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/math/spacing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-library/src/math/spacing.rs')
-rw-r--r--crates/typst-library/src/math/spacing.rs60
1 files changed, 60 insertions, 0 deletions
diff --git a/crates/typst-library/src/math/spacing.rs b/crates/typst-library/src/math/spacing.rs
new file mode 100644
index 00000000..848aca78
--- /dev/null
+++ b/crates/typst-library/src/math/spacing.rs
@@ -0,0 +1,60 @@
+use super::*;
+
+pub(super) const THIN: Em = Em::new(1.0 / 6.0);
+pub(super) const MEDIUM: Em = Em::new(2.0 / 9.0);
+pub(super) const THICK: Em = Em::new(5.0 / 18.0);
+pub(super) const QUAD: Em = Em::new(1.0);
+
+/// Hook up all spacings.
+pub(super) fn define(math: &mut Scope) {
+ math.define("thin", HElem::new(THIN.into()).pack());
+ math.define("med", HElem::new(MEDIUM.into()).pack());
+ math.define("thick", HElem::new(THICK.into()).pack());
+ math.define("quad", HElem::new(QUAD.into()).pack());
+}
+
+/// Create the spacing between two fragments in a given style.
+pub(super) fn spacing(
+ l: &MathFragment,
+ space: Option<MathFragment>,
+ r: &MathFragment,
+) -> Option<MathFragment> {
+ use MathClass::*;
+
+ let class = |f: &MathFragment| f.class().unwrap_or(Special);
+ let resolve = |v: Em, f: &MathFragment| {
+ Some(MathFragment::Spacing(f.font_size().map_or(Abs::zero(), |size| v.at(size))))
+ };
+ let script =
+ |f: &MathFragment| f.style().map_or(false, |s| s.size <= MathSize::Script);
+
+ match (class(l), class(r)) {
+ // No spacing before punctuation; thin spacing after punctuation, unless
+ // in script size.
+ (_, Punctuation) => None,
+ (Punctuation, _) if !script(l) => resolve(THIN, l),
+
+ // No spacing after opening delimiters and before closing delimiters.
+ (Opening, _) | (_, Closing) => None,
+
+ // Thick spacing around relations, unless followed by a another relation
+ // or in script size.
+ (Relation, Relation) => None,
+ (Relation, _) if !script(l) => resolve(THICK, l),
+ (_, Relation) if !script(r) => resolve(THICK, r),
+
+ // Medium spacing around binary operators, unless in script size.
+ (Binary, _) if !script(l) => resolve(MEDIUM, l),
+ (_, Binary) if !script(r) => resolve(MEDIUM, r),
+
+ // Thin spacing around large operators, unless next to a delimiter.
+ (Large, Opening | Fence) | (Closing | Fence, Large) => None,
+ (Large, _) => resolve(THIN, l),
+ (_, Large) => resolve(THIN, r),
+
+ // Spacing around spaced frames.
+ _ if (l.is_spaced() || r.is_spaced()) => space,
+
+ _ => None,
+ }
+}