Symbol values and modules
This commit is contained in:
parent
13efa128c8
commit
1de53730bc
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -974,11 +974,6 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symmie"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/typst/symmie#75755e62895fcd822b3a1038462f4548b18b8119"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
@ -1107,7 +1102,6 @@ dependencies = [
|
||||
"siphasher",
|
||||
"subsetter",
|
||||
"svg2pdf",
|
||||
"symmie",
|
||||
"thin-vec",
|
||||
"tiny-skia",
|
||||
"ttf-parser 0.18.1",
|
||||
@ -1152,7 +1146,6 @@ dependencies = [
|
||||
"roxmltree",
|
||||
"rustybuzz",
|
||||
"serde_json",
|
||||
"symmie",
|
||||
"syntect",
|
||||
"ttf-parser 0.18.1",
|
||||
"typed-arena",
|
||||
|
@ -32,7 +32,6 @@ serde = { version = "1", features = ["derive"] }
|
||||
siphasher = "0.3"
|
||||
subsetter = "0.1"
|
||||
svg2pdf = "0.4"
|
||||
symmie = { git = "https://github.com/typst/symmie" }
|
||||
thin-vec = "0.2"
|
||||
tiny-skia = "0.6.2"
|
||||
ttf-parser = "0.18.1"
|
||||
|
@ -20,7 +20,6 @@ once_cell = "1"
|
||||
roxmltree = "0.14"
|
||||
rustybuzz = "0.5"
|
||||
serde_json = "1"
|
||||
symmie = { git = "https://github.com/typst/symmie" }
|
||||
syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] }
|
||||
ttf-parser = "0.18.1"
|
||||
typed-arena = "2"
|
||||
|
@ -17,13 +17,14 @@ use self::layout::LayoutRoot;
|
||||
|
||||
/// Construct the standard library.
|
||||
pub fn build() -> Library {
|
||||
let math = math::module();
|
||||
let global = global(math.clone());
|
||||
let sym = text::sym();
|
||||
let math = math::module(&sym);
|
||||
let global = global(sym, math.clone());
|
||||
Library { global, math, styles: styles(), items: items() }
|
||||
}
|
||||
|
||||
/// Construct the module with global definitions.
|
||||
fn global(math: Module) -> Module {
|
||||
fn global(sym: Module, math: Module) -> Module {
|
||||
let mut global = Scope::deduplicating();
|
||||
|
||||
// Basics.
|
||||
@ -36,7 +37,6 @@ fn global(math: Module) -> Module {
|
||||
// Text.
|
||||
global.def_func::<text::TextNode>("text");
|
||||
global.def_func::<text::LinebreakNode>("linebreak");
|
||||
global.def_func::<text::SymbolNode>("symbol");
|
||||
global.def_func::<text::SmartQuoteNode>("smartquote");
|
||||
global.def_func::<text::StrongNode>("strong");
|
||||
global.def_func::<text::EmphNode>("emph");
|
||||
@ -49,6 +49,8 @@ fn global(math: Module) -> Module {
|
||||
global.def_func::<text::StrikeNode>("strike");
|
||||
global.def_func::<text::OverlineNode>("overline");
|
||||
global.def_func::<text::RawNode>("raw");
|
||||
global.define("sym", sym);
|
||||
global.define("emoji", text::emoji());
|
||||
|
||||
// Math.
|
||||
global.define("math", math);
|
||||
@ -169,7 +171,6 @@ fn items() -> LangItems {
|
||||
text: |text| text::TextNode(text).pack(),
|
||||
text_id: NodeId::of::<text::TextNode>(),
|
||||
text_str: |content| Some(&content.to::<text::TextNode>()?.0),
|
||||
symbol: |notation| text::SymbolNode(notation).pack(),
|
||||
smart_quote: |double| text::SmartQuoteNode { double }.pack(),
|
||||
parbreak: || layout::ParbreakNode.pack(),
|
||||
strong: |body| text::StrongNode(body).pack(),
|
||||
|
@ -17,6 +17,7 @@ mod script;
|
||||
mod spacing;
|
||||
mod stretch;
|
||||
mod style;
|
||||
mod symbols;
|
||||
|
||||
pub use self::accent::*;
|
||||
pub use self::align::*;
|
||||
@ -46,10 +47,10 @@ use crate::prelude::*;
|
||||
use crate::text::LinebreakNode;
|
||||
use crate::text::TextNode;
|
||||
use crate::text::TextSize;
|
||||
use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode, SymbolNode};
|
||||
use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode};
|
||||
|
||||
/// Create a module with all math definitions.
|
||||
pub fn module() -> Module {
|
||||
pub fn module(sym: &Module) -> Module {
|
||||
let mut math = Scope::deduplicating();
|
||||
math.def_func::<FormulaNode>("formula");
|
||||
math.def_func::<LrNode>("lr");
|
||||
@ -77,7 +78,9 @@ pub fn module() -> Module {
|
||||
math.def_func::<MonoNode>("mono");
|
||||
math.def_func::<BbNode>("bb");
|
||||
spacing::define(&mut math);
|
||||
symbols::define(&mut math);
|
||||
op::define(&mut math);
|
||||
math.copy_from(sym.scope());
|
||||
Module::new("math").with_scope(math)
|
||||
}
|
||||
|
||||
@ -227,14 +230,6 @@ impl LayoutMath for Content {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(node) = self.to::<SymbolNode>() {
|
||||
if let Some(c) = symmie::get(&node.0) {
|
||||
return AtomNode(c.into()).layout_math(ctx);
|
||||
} else if let Some(span) = self.span() {
|
||||
bail!(span, "unknown symbol");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(node) = self.to::<SequenceNode>() {
|
||||
for child in &node.0 {
|
||||
child.layout_math(ctx)?;
|
||||
|
425
library/src/math/symbols.rs
Normal file
425
library/src/math/symbols.rs
Normal file
@ -0,0 +1,425 @@
|
||||
use typst::model::symbols;
|
||||
|
||||
symbols! {
|
||||
define,
|
||||
|
||||
// Lowercase Greek.
|
||||
alpha: 'α',
|
||||
beta: ['β', "alt": 'ϐ'],
|
||||
chi: 'χ',
|
||||
delta: 'δ',
|
||||
epsilon: ['ε', "alt": 'ϵ'],
|
||||
eta: 'η',
|
||||
gamma: 'γ',
|
||||
iota: 'ι',
|
||||
kai: 'ϗ',
|
||||
kappa: ['κ', "alt": 'ϰ'],
|
||||
lambda: 'λ',
|
||||
mu: 'μ',
|
||||
nu: 'ν',
|
||||
ohm: ['Ω', "inv": '℧'],
|
||||
omega: 'ω',
|
||||
omicron: 'ο',
|
||||
phi: ['φ', "alt": 'ϕ'],
|
||||
pi: ['π', "alt": 'ϖ'],
|
||||
psi: 'ψ',
|
||||
rho: ['ρ', "alt": 'ϱ'],
|
||||
sigma: 'σ',
|
||||
tau: 'τ',
|
||||
theta: ['θ', "alt": 'ϑ'],
|
||||
upsilon: 'υ',
|
||||
xi: 'ξ',
|
||||
zeta: 'ζ',
|
||||
|
||||
// Uppercase Greek.
|
||||
Alpha: 'Α',
|
||||
Beta: 'Β',
|
||||
Chi: 'Χ',
|
||||
Delta: 'Δ',
|
||||
Epsilon: 'Ε',
|
||||
Eta: 'Η',
|
||||
Gamma: 'Γ',
|
||||
Iota: 'Ι',
|
||||
Kai: 'Ϗ',
|
||||
Kappa: 'Κ',
|
||||
Lambda: 'Λ',
|
||||
Mu: 'Μ',
|
||||
Nu: 'Ν',
|
||||
Omega: 'Ω',
|
||||
Omicron: 'Ο',
|
||||
Phi: 'Φ',
|
||||
Pi: 'Π',
|
||||
Psi: 'Ψ',
|
||||
Rho: 'Ρ',
|
||||
Sigma: 'Σ',
|
||||
Tau: 'Τ',
|
||||
Theta: 'Θ',
|
||||
Upsilon: 'Υ',
|
||||
Xi: 'Ξ',
|
||||
Zeta: 'Ζ',
|
||||
|
||||
// Hebrew.
|
||||
alef: 'א',
|
||||
bet: 'ב',
|
||||
gimel: 'ג',
|
||||
shin: 'ש',
|
||||
|
||||
// Double-struck.
|
||||
AA: '𝔸',
|
||||
BB: '𝔹',
|
||||
CC: 'ℂ',
|
||||
DD: '𝔻',
|
||||
EE: '𝔼',
|
||||
FF: '𝔽',
|
||||
GG: '𝔾',
|
||||
HH: 'ℍ',
|
||||
II: '𝕀',
|
||||
JJ: '𝕁',
|
||||
KK: '𝕂',
|
||||
LL: '𝕃',
|
||||
MM: '𝕄',
|
||||
NN: 'ℕ',
|
||||
OO: '𝕆',
|
||||
PP: 'ℙ',
|
||||
QQ: 'ℚ',
|
||||
RR: 'ℝ',
|
||||
SS: '𝕊',
|
||||
TT: '𝕋',
|
||||
UU: '𝕌',
|
||||
VV: '𝕍',
|
||||
WW: '𝕎',
|
||||
XX: '𝕏',
|
||||
YY: '𝕐',
|
||||
ZZ: 'ℤ',
|
||||
|
||||
// Override.
|
||||
angled: [
|
||||
"l": '⟨',
|
||||
"l.double": '⟪',
|
||||
"r": '⟩',
|
||||
"r.double": '⟫',
|
||||
],
|
||||
|
||||
// Operators.
|
||||
plus: [
|
||||
'+',
|
||||
"circle": '⊕',
|
||||
"circle.arrow": '⟴',
|
||||
"circle.big": '⨁',
|
||||
"dot": '∔',
|
||||
"minus": '±',
|
||||
"small": '﹢',
|
||||
"square": '⊞',
|
||||
"triangle": '⨹',
|
||||
],
|
||||
minus: [
|
||||
'−',
|
||||
"circle": '⊖',
|
||||
"dot": '∸',
|
||||
"plus": '∓',
|
||||
"square": '⊟',
|
||||
"tilde": '≂',
|
||||
"triangle": '⨺',
|
||||
],
|
||||
dot: [
|
||||
'⋅',
|
||||
"circle": '⊙',
|
||||
"circle.big": '⨀',
|
||||
"square": '⊡',
|
||||
],
|
||||
div: [
|
||||
'∕',
|
||||
"circle": '⊘',
|
||||
"sign": '÷',
|
||||
"sign.circle": '⨸',
|
||||
],
|
||||
times: [
|
||||
'×',
|
||||
"big": '⨉',
|
||||
"circle": '⊗',
|
||||
"circle.big": '⨂',
|
||||
"div": '⋇',
|
||||
"l": '⋋',
|
||||
"r": '⋌',
|
||||
"square": '⊠',
|
||||
"triangle": '⨻',
|
||||
],
|
||||
ast: [
|
||||
'∗',
|
||||
"circle": '⊛',
|
||||
"small": '﹡',
|
||||
"sq": '⧆',
|
||||
],
|
||||
star: '⋆',
|
||||
smash: '⨳',
|
||||
wreath: '≀',
|
||||
|
||||
// Relations.
|
||||
eq: [
|
||||
'=',
|
||||
"ast": '≛',
|
||||
"circle": '⊜',
|
||||
"colon": '≕',
|
||||
"def": '≝',
|
||||
"delta": '≜',
|
||||
"equi": '≚',
|
||||
"est": '≙',
|
||||
"gt": '⋝',
|
||||
"lt": '⋜',
|
||||
"m": '≞',
|
||||
"not": '≠',
|
||||
"prec": '⋞',
|
||||
"quest": '≟',
|
||||
"small": '﹦',
|
||||
"succ": '⋟',
|
||||
],
|
||||
gt: [
|
||||
'>',
|
||||
"circle": '⧁',
|
||||
"dot": '⋗',
|
||||
"double": '≫',
|
||||
"eq": '≥',
|
||||
"eq.lt": '⋛',
|
||||
"eq.not": '≱',
|
||||
"eqq": '≧',
|
||||
"lt": '≷',
|
||||
"lt.not": '≹',
|
||||
"neqq": '≩',
|
||||
"not": '≯',
|
||||
"ntilde": '⋧',
|
||||
"small": '﹥',
|
||||
"tilde": '≳',
|
||||
"tilde.not": '≵',
|
||||
"triple": '⋙',
|
||||
"triple.nested": '⫸',
|
||||
],
|
||||
lt: [
|
||||
'<',
|
||||
"circle": '⧀',
|
||||
"dot": '⋖',
|
||||
"double": '≪',
|
||||
"eq": '≤',
|
||||
"eq.gt": '⋚',
|
||||
"eq.not": '≰',
|
||||
"eqq": '≦',
|
||||
"gt": '≶',
|
||||
"gt.not": '≸',
|
||||
"neqq": '≨',
|
||||
"not": '≮',
|
||||
"ntilde": '⋦',
|
||||
"small": '﹤',
|
||||
"tilde": '≲',
|
||||
"tilde.not": '≴',
|
||||
"triple": '⋘',
|
||||
"triple.nested": '⫷',
|
||||
],
|
||||
prec: [
|
||||
'≺',
|
||||
"approx": '⪷',
|
||||
"double": '⪻',
|
||||
"eq": '≼',
|
||||
"eq.not": '⋠',
|
||||
"eqq": '⪳',
|
||||
"napprox": '⪹',
|
||||
"neqq": '⪵',
|
||||
"not": '⊀',
|
||||
"ntilde": '⋨',
|
||||
"tilde": '≾',
|
||||
],
|
||||
succ: [
|
||||
'≻',
|
||||
"approx": '⪸',
|
||||
"double": '⪼',
|
||||
"eq": '≽',
|
||||
"eq.not": '⋡',
|
||||
"eqq": '⪴',
|
||||
"napprox": '⪺',
|
||||
"neqq": '⪶',
|
||||
"not": '⊁',
|
||||
"ntilde": '⋩',
|
||||
"tilde": '≿',
|
||||
],
|
||||
ident: ['≡', "not": '≢', "strict": '≣'],
|
||||
approx: ['≈', "eq": '≊', "not": '≉'],
|
||||
tilde: [
|
||||
'∼',
|
||||
"eq": '≃',
|
||||
"eq.not": '≄',
|
||||
"eq.rev": '⋍',
|
||||
"eqq": '≅',
|
||||
"eqq.not": '≇',
|
||||
"neqq": '≆',
|
||||
"not": '≁',
|
||||
"rev": '∽',
|
||||
"rev.eqq": '≌',
|
||||
"triple": '≋',
|
||||
],
|
||||
|
||||
// Set theory.
|
||||
nothing: ['∅', "rev": '⦰'],
|
||||
in: [
|
||||
'∈',
|
||||
"not": '∉',
|
||||
"rev": '∋',
|
||||
"rev.not": '∌',
|
||||
"rev.small": '∍',
|
||||
"small": '∊',
|
||||
],
|
||||
subset: [
|
||||
'⊂',
|
||||
"dot": '⪽',
|
||||
"double": '⋐',
|
||||
"eq": '⊆',
|
||||
"eq.not": '⊈',
|
||||
"eq.sq": '⊑',
|
||||
"eq.sq.not": '⋢',
|
||||
"neq": '⊊',
|
||||
"not": '⊄',
|
||||
"sq": '⊏',
|
||||
"sq.neq": '⋤',
|
||||
],
|
||||
supset: [
|
||||
'⊃',
|
||||
"dot": '⪾',
|
||||
"double": '⋑',
|
||||
"eq": '⊇',
|
||||
"eq.not": '⊉',
|
||||
"eq.sq": '⊒',
|
||||
"eq.sq.not": '⋣',
|
||||
"neq": '⊋',
|
||||
"not": '⊅',
|
||||
"sq": '⊐',
|
||||
"sq.neq": '⋥',
|
||||
],
|
||||
union: [
|
||||
'∪',
|
||||
"arrow": '⊌',
|
||||
"big": '⋃',
|
||||
"dot": '⊍',
|
||||
"dot.big": '⨃',
|
||||
"double": '⋓',
|
||||
"minus": '⩁',
|
||||
"or": '⩅',
|
||||
"plus": '⊎',
|
||||
"plus.big": '⨄',
|
||||
"sq": '⊔',
|
||||
"sq.big": '⨆',
|
||||
"sq.double": '⩏',
|
||||
],
|
||||
sect: [
|
||||
'∩',
|
||||
"and": '⩄',
|
||||
"big": '⋂',
|
||||
"dot": '⩀',
|
||||
"double": '⋒',
|
||||
"sq": '⊓',
|
||||
"sq.big": '⨅',
|
||||
"sq.double": '⩎',
|
||||
],
|
||||
without: '∖',
|
||||
complement: '∁',
|
||||
|
||||
// Relational algebra.
|
||||
join: [
|
||||
'⨝',
|
||||
"r": '⟖',
|
||||
"l": '⟕',
|
||||
"l.r": '⟗',
|
||||
],
|
||||
|
||||
// Logic.
|
||||
forall: '∀',
|
||||
exists: ['∃', "not": '∄'],
|
||||
top: '⟙',
|
||||
bot: '⊥',
|
||||
not: '¬',
|
||||
and: [
|
||||
'∧',
|
||||
"big": '⋀',
|
||||
"curly": '⋏',
|
||||
"dot": '⟑',
|
||||
"double": '⩓',
|
||||
],
|
||||
or: [
|
||||
'∨',
|
||||
"big": '⋁',
|
||||
"curly": '⋎',
|
||||
"dot": '⟇',
|
||||
"double": '⩔',
|
||||
],
|
||||
models: '⊧',
|
||||
therefore: '∴',
|
||||
because: '∵',
|
||||
qed: '∎',
|
||||
tack: [
|
||||
"r": '⊢',
|
||||
"r.long": '⟝',
|
||||
"l": '⊣',
|
||||
"l.long": '⟞',
|
||||
"l.short": '⫞',
|
||||
"t": '⊥',
|
||||
"t.big": '⟘',
|
||||
"t.double": '⫫',
|
||||
"t.short": '⫠',
|
||||
"b": '⊤',
|
||||
"b.big": '⟙',
|
||||
"b.double": '⫪',
|
||||
"b.short": '⫟',
|
||||
"l.r": '⟛',
|
||||
],
|
||||
|
||||
// Geometry.
|
||||
perp: ['⟂', "circle": '⦹'],
|
||||
parallel: ['∥', "circle": '⦷', "not": '∦'],
|
||||
angle: [
|
||||
'∠',
|
||||
"acute": '⦟',
|
||||
"arc": '∡',
|
||||
"arc.rev": '⦛',
|
||||
"rev": '⦣',
|
||||
"right": '∟',
|
||||
"right.rev": '⯾',
|
||||
"right.arc": '⊾',
|
||||
"right.dot": '⦝',
|
||||
"right.sq": '⦜',
|
||||
"spatial": '⟀',
|
||||
"spheric": '∢',
|
||||
"spheric.rev": '⦠',
|
||||
"spheric.top": '⦡',
|
||||
],
|
||||
|
||||
// Analysis.
|
||||
infty: '∞',
|
||||
sum: ['∑', "integral": '⨋'],
|
||||
prod: ['∏', "co": '∐'],
|
||||
integral: [
|
||||
'∫',
|
||||
"arrow.hook": '⨗',
|
||||
"ccw": '⨑',
|
||||
"cont": '∮',
|
||||
"cont.ccw": '∳',
|
||||
"cont.cw": '∲',
|
||||
"cw": '∱',
|
||||
"double": '∬',
|
||||
"quad": '⨌',
|
||||
"sect": '⨙',
|
||||
"sq": '⨖',
|
||||
"surf": '∯',
|
||||
"times": '⨘',
|
||||
"triple": '∭',
|
||||
"union": '⨚',
|
||||
"vol": '∰',
|
||||
],
|
||||
prop: '∝',
|
||||
divides: ['∣', "not": '∤'],
|
||||
|
||||
// Miscellaneous.
|
||||
diff: '∂',
|
||||
nabla: '∇',
|
||||
kelvin: 'K',
|
||||
micro: 'µ',
|
||||
degree: ['°', "c": '℃', "f": '℉'],
|
||||
planck: ['ℎ', "reduce": 'ℏ'],
|
||||
angstrom: 'Å',
|
||||
diameter: '⌀',
|
||||
}
|
1355
library/src/text/emoji.rs
Normal file
1355
library/src/text/emoji.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,22 @@
|
||||
//! Text handling.
|
||||
|
||||
mod deco;
|
||||
mod emoji;
|
||||
mod misc;
|
||||
mod quotes;
|
||||
mod raw;
|
||||
mod shaping;
|
||||
mod shift;
|
||||
mod symbol;
|
||||
mod symbols;
|
||||
|
||||
pub use self::deco::*;
|
||||
pub use self::emoji::*;
|
||||
pub use self::misc::*;
|
||||
pub use self::quotes::*;
|
||||
pub use self::raw::*;
|
||||
pub use self::shaping::*;
|
||||
pub use self::shift::*;
|
||||
pub use self::symbol::*;
|
||||
pub use self::symbols::*;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -1,111 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// # Symbol
|
||||
/// A symbol identified by symmie notation.
|
||||
///
|
||||
/// Symmie is Typst's notation for Unicode symbols. It is based on the idea of
|
||||
/// _modifiers._ Many symbols in Unicode are very similar. In symmie, such
|
||||
/// groups of symbols share a common name. To distinguish between the symbols
|
||||
/// within a group, we use one or multiple modifiers that are separated from the
|
||||
/// name by colons.
|
||||
///
|
||||
/// There is currently no easily viewable list of all names, but in the
|
||||
/// meantime you can rely on the autocompletion in Typst's web editor.
|
||||
///
|
||||
/// ## Syntax
|
||||
/// This function also has dedicated syntax: In markup, you can enclose symmie
|
||||
/// notation within colons to produce a symbol. And in math, you can just write
|
||||
/// the notation directly. There, all letter sequence of length at least two are
|
||||
/// automatically parsed as symbols (unless a variable of that name is defined).
|
||||
///
|
||||
/// Additionally, some very common but hard to type symbols can be expressed with
|
||||
/// dedicated shortcuts. These are:
|
||||
///
|
||||
/// | Symmie | Shorthand | Result |
|
||||
/// | ----------- | --------- | ------ |
|
||||
/// | `dots:b` | `...` | … |
|
||||
/// | `dash:en` | `---` | – |
|
||||
/// | `dash:em` | `--` | — |
|
||||
/// | none yet | `-?` | A soft hyphen |
|
||||
/// | none yet | `~` | A non breaking space |
|
||||
///
|
||||
/// Within math mode, additional shorthands are available:
|
||||
///
|
||||
/// | Symmie | Shorthand | Result |
|
||||
/// | ------------------ | --------- | ------ |
|
||||
/// | `arrow:r` | `->` | `→` |
|
||||
/// | `arrow:r:double` | `=>` | `⇒` |
|
||||
/// | `arrow:l` | `<-` | `←` |
|
||||
/// | `arrow:r:bar` | <code>|-></code> | `↦` |
|
||||
/// | `arrow:l:r` | `<->` | `↔` |
|
||||
/// | `arrow:l:r:double` | `<=>` | `⇔` |
|
||||
/// | `eq:not` | `!=` | `≠` |
|
||||
/// | `eq:gt` | `>=` | `≥` |
|
||||
/// | `eq:lt` | `<=` | `≤` |
|
||||
/// | `colon:eq` | `:=` | `≔` |
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// // In text, with colons.
|
||||
/// :arrow:l: \
|
||||
/// :arrow:r: \
|
||||
/// :arrow:t: \
|
||||
/// :turtle: \
|
||||
/// :face:halo: \
|
||||
/// :woman:old:
|
||||
///
|
||||
/// // In math, directly.
|
||||
/// $f : NN -> RR$ \
|
||||
/// $A sub:eq B without C$ \
|
||||
/// $a times:div b eq:not c$
|
||||
/// ```
|
||||
///
|
||||
/// ## Parameters
|
||||
/// - notation: EcoString (positional, required)
|
||||
/// The symbol's symmie notation.
|
||||
///
|
||||
/// Consists of a name, followed by a number colon-separated modifiers
|
||||
/// in no particular order.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// #symbol("NN") \
|
||||
/// #symbol("face:grin")
|
||||
/// ```
|
||||
///
|
||||
/// ## Category
|
||||
/// text
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SymbolNode(pub EcoString);
|
||||
|
||||
#[node]
|
||||
impl SymbolNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("notation")?).pack())
|
||||
}
|
||||
|
||||
fn field(&self, name: &str) -> Option<Value> {
|
||||
match name {
|
||||
"notation" => Some(Value::Str(self.0.clone().into())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for SymbolNode {
|
||||
fn show(&self, _: &mut Vt, this: &Content, _: StyleChain) -> SourceResult<Content> {
|
||||
match symmie::get(&self.0) {
|
||||
Some(c) => Ok(TextNode::packed(c)),
|
||||
None => {
|
||||
if let Some(span) = this.span() {
|
||||
bail!(span, "unknown symbol");
|
||||
}
|
||||
|
||||
Ok(Content::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
460
library/src/text/symbols.rs
Normal file
460
library/src/text/symbols.rs
Normal file
@ -0,0 +1,460 @@
|
||||
use typst::model::{symbols, Module, Scope};
|
||||
|
||||
/// A module with all symbols.
|
||||
pub fn sym() -> Module {
|
||||
let mut scope = Scope::new();
|
||||
define(&mut scope);
|
||||
Module::new("sym").with_scope(scope)
|
||||
}
|
||||
|
||||
symbols! {
|
||||
define,
|
||||
|
||||
// Space and control.
|
||||
space: [
|
||||
' ',
|
||||
"em": '\u{2003}',
|
||||
"en": '\u{2002}',
|
||||
"fig": '\u{2007}',
|
||||
"hair": '\u{200A}',
|
||||
"ideo": '\u{3000}',
|
||||
"nobreak": '\u{A0}',
|
||||
"punct": '\u{2008}',
|
||||
"quarter": '\u{2005}',
|
||||
"sixth": '\u{2006}',
|
||||
"thin": '\u{2009}',
|
||||
"third": '\u{2004}',
|
||||
],
|
||||
wj: '\u{2060}',
|
||||
zwj: '\u{200D}',
|
||||
zwnj: '\u{200C}',
|
||||
zws: '\u{200B}',
|
||||
|
||||
// Punctuation.
|
||||
dot: ['.', "c": '·'],
|
||||
dots: [
|
||||
"h": '…',
|
||||
"h.c": '⋯',
|
||||
"v": '⋮',
|
||||
"down": '⋱',
|
||||
"up": '⋰',
|
||||
],
|
||||
colon: [
|
||||
':',
|
||||
"eq": '≔',
|
||||
"double.eq": '⩴',
|
||||
],
|
||||
comma: ',',
|
||||
semi: [';', "rev": '⁏'],
|
||||
quest: ['?', "double": '⁇', "excl": '⁈', "inv": '¿'],
|
||||
excl: ['!', "double": '‼', "inv": '¡', "quest": '⁉'],
|
||||
interrobang: '‽',
|
||||
hash: '#',
|
||||
at: '@',
|
||||
section: '§',
|
||||
percent: '%',
|
||||
permille: '‰',
|
||||
co: '℅',
|
||||
pilcrow: ['¶', "rev": '⁋'],
|
||||
dagger: ['†', "double": '‡'],
|
||||
slash: ['/', "double": '⫽', "triple": '⫻'],
|
||||
backslash: ['\\', "circle": '⦸', "not": '⧷'],
|
||||
ast: ['*', "low": '⁎', "double": '⁑', "triple": '⁂'],
|
||||
amp: ['&', "inv": '⅋'],
|
||||
dash: [
|
||||
"en": '–',
|
||||
"em": '—',
|
||||
"fig": '‒',
|
||||
"wave": '〜',
|
||||
"colon": '∹',
|
||||
"circle": '⊝',
|
||||
"wave.double": '〰',
|
||||
],
|
||||
hyph: [
|
||||
'‐',
|
||||
"minus": '\u{2D}',
|
||||
"nobreak": '\u{2011}',
|
||||
"point": '‧',
|
||||
"soft": '\u{ad}',
|
||||
],
|
||||
prime: [
|
||||
'′',
|
||||
"rev": '‵',
|
||||
"double": '″',
|
||||
"double.rev": '‶',
|
||||
"triple": '‴',
|
||||
"triple.rev": '‷',
|
||||
"quad": '⁗',
|
||||
],
|
||||
quote: [
|
||||
"double": '"',
|
||||
"single": '\'',
|
||||
"l.double": '“',
|
||||
"l.single": '‘',
|
||||
"r.double": '”',
|
||||
"r.single": '’',
|
||||
"angle.l.double": '«',
|
||||
"angle.l.single": '‹',
|
||||
"angle.r.double": '»',
|
||||
"angle.r.single": '›',
|
||||
"high.double": '‟',
|
||||
"high.single": '‛',
|
||||
"low.double": '„',
|
||||
"low.single": '‚',
|
||||
],
|
||||
|
||||
// Delimiters.
|
||||
paren: [
|
||||
"l": '(',
|
||||
"r": ')',
|
||||
"t": '⏜',
|
||||
"b": '⏝',
|
||||
],
|
||||
bracket: [
|
||||
"l": '[',
|
||||
"r": ']',
|
||||
"t": '⎴',
|
||||
"b": '⎵',
|
||||
],
|
||||
brace: [
|
||||
"l": '{',
|
||||
"r": '}',
|
||||
"t": '⏞',
|
||||
"b": '⏟',
|
||||
],
|
||||
turtle: [
|
||||
"l": '〔',
|
||||
"r": '〕',
|
||||
"t": '⏠',
|
||||
"b": '⏡',
|
||||
],
|
||||
angled: ["l": '〈', "r": '〉'],
|
||||
bar: [
|
||||
"v": '|',
|
||||
"v.double": '‖',
|
||||
"v.triple": '⦀',
|
||||
"v.broken": '¦',
|
||||
"v.circle": '⦶',
|
||||
"h": '―',
|
||||
],
|
||||
fence: [
|
||||
"l": '⧘',
|
||||
"l.double": '⧚',
|
||||
"r": '⧙',
|
||||
"r.double": '⧛',
|
||||
"dotted": '⦙',
|
||||
],
|
||||
|
||||
// Accents.
|
||||
acute: ['´', "double": '˝'],
|
||||
breve: '˘',
|
||||
caret: '‸',
|
||||
caron: 'ˇ',
|
||||
cedilla: '¸',
|
||||
circum: '^',
|
||||
diaer: '¨',
|
||||
grave: '`',
|
||||
macron: '¯',
|
||||
tilde: '~',
|
||||
overline: '‾',
|
||||
|
||||
// Currency.
|
||||
bitcoin: '₿',
|
||||
dollar: '$',
|
||||
euro: '€',
|
||||
franc: '₣',
|
||||
lira: '₺',
|
||||
peso: '₱',
|
||||
pound: '£',
|
||||
ruble: '₽',
|
||||
rupee: '₹',
|
||||
won: '₩',
|
||||
yen: '¥',
|
||||
|
||||
// Miscellaneous.
|
||||
ballot: ['☐', "x": '☒'],
|
||||
checkmark: ['✓', "light": '🗸'],
|
||||
copyright: ['©', "sound": '℗'],
|
||||
floral: ['❦', "l": '☙', "r": '❧'],
|
||||
notes: ["up": '🎜', "down": '🎝'],
|
||||
refmark: '※',
|
||||
servicemark: '℠',
|
||||
maltese: '✠',
|
||||
suit: [
|
||||
"club": '♣',
|
||||
"diamond": '♦',
|
||||
"heart": '♥',
|
||||
"spade": '♠',
|
||||
],
|
||||
|
||||
// Shapes.
|
||||
circle: [
|
||||
"stroked": '○',
|
||||
"stroked.tiny": '∘',
|
||||
"stroked.small": '⚬',
|
||||
"stroked.big": '◯',
|
||||
"filled": '●',
|
||||
"filled.tiny": '⦁',
|
||||
"filled.small": '∙',
|
||||
"filled.big": '⬤',
|
||||
"dotted": '◌',
|
||||
"nested": '⊚',
|
||||
],
|
||||
square: [
|
||||
"stroked": '□',
|
||||
"stroked.tiny": '▫',
|
||||
"stroked.small": '◽',
|
||||
"stroked.medium": '◻',
|
||||
"stroked.big": '⬜',
|
||||
"stroked.dotted": '⬚',
|
||||
"stroked.rounded": '▢',
|
||||
"filled": '■',
|
||||
"filled.tiny": '▪',
|
||||
"filled.small": '◾',
|
||||
"filled.medium": '◼',
|
||||
"filled.big": '⬛',
|
||||
],
|
||||
ellipse: [
|
||||
"stroked.h": '⬭',
|
||||
"stroked.v": '⬯',
|
||||
"filled.h": '⬬',
|
||||
"filled.v": '⬮',
|
||||
],
|
||||
rect: [
|
||||
"stroked.h": '▭',
|
||||
"stroked.v": '▯',
|
||||
"filled.h": '▬',
|
||||
"filled.v": '▮',
|
||||
],
|
||||
triangle: [
|
||||
"stroked.r": '▷',
|
||||
"stroked.l": '◁',
|
||||
"stroked.t": '△',
|
||||
"stroked.b": '▽',
|
||||
"stroked.bl": '◺',
|
||||
"stroked.br": '◿',
|
||||
"stroked.tl": '◸',
|
||||
"stroked.tr": '◹',
|
||||
"stroked.small.r": '▹',
|
||||
"stroked.small.b": '▿',
|
||||
"stroked.small.l": '◃',
|
||||
"stroked.small.t": '▵',
|
||||
"stroked.rounded": '🛆',
|
||||
"stroked.nested": '⟁',
|
||||
"stroked.dot": '◬',
|
||||
"filled.r": '▶',
|
||||
"filled.l": '◀',
|
||||
"filled.t": '▲',
|
||||
"filled.b": '▼',
|
||||
"filled.bl": '◣',
|
||||
"filled.br": '◢',
|
||||
"filled.tl": '◤',
|
||||
"filled.tr": '◥',
|
||||
"filled.small.r": '▸',
|
||||
"filled.small.b": '▾',
|
||||
"filled.small.l": '◂',
|
||||
"filled.small.t": '▴',
|
||||
],
|
||||
diamond: [
|
||||
"stroked": '◇',
|
||||
"stroked.small": '⋄',
|
||||
"stroked.medium": '⬦',
|
||||
"stroked.dot": '⟐',
|
||||
"filled": '◆',
|
||||
"filled.medium": '⬥',
|
||||
"filled.small": '⬩',
|
||||
],
|
||||
lozenge: [
|
||||
"stroked": '◊',
|
||||
"stroked.small": '⬫',
|
||||
"stroked.medium": '⬨',
|
||||
"filled": '⧫',
|
||||
"filled.small": '⬪',
|
||||
"filled.medium": '⬧',
|
||||
],
|
||||
penta: ["stroked": '⬠', "filled": '⬟'],
|
||||
hexa: ["stroked": '⬡', "filled": '⬢'],
|
||||
|
||||
// Arrows and harpoons.
|
||||
arrow: [
|
||||
"r": '→',
|
||||
"r.long.bar": '⟼',
|
||||
"r.bar": '↦',
|
||||
"r.curve": '⤷',
|
||||
"r.dashed": '⇢',
|
||||
"r.dotted": '⤑',
|
||||
"r.double": '⇒',
|
||||
"r.double.bar": '⤇',
|
||||
"r.double.long": '⟹',
|
||||
"r.double.long.bar": '⟾',
|
||||
"r.double.not": '⇏',
|
||||
"r.filled": '➡',
|
||||
"r.hook": '↪',
|
||||
"r.long": '⟶',
|
||||
"r.long.squiggly": '⟿',
|
||||
"r.loop": '↬',
|
||||
"r.not": '↛',
|
||||
"r.quad": '⭆',
|
||||
"r.squiggly": '⇝',
|
||||
"r.stop": '⇥',
|
||||
"r.stroked": '⇨',
|
||||
"r.tail": '↣',
|
||||
"r.triple": '⇛',
|
||||
"r.twohead.bar": '⤅',
|
||||
"r.twohead": '↠',
|
||||
"r.wave": '↝',
|
||||
"l": '←',
|
||||
"l.bar": '↤',
|
||||
"l.curve": '⤶',
|
||||
"l.dashed": '⇠',
|
||||
"l.dotted": '⬸',
|
||||
"l.double": '⇐',
|
||||
"l.double.bar": '⤆',
|
||||
"l.double.long": '⟸',
|
||||
"l.double.long.bar": '⟽',
|
||||
"l.double.not": '⇍',
|
||||
"l.filled": '⬅',
|
||||
"l.hook": '↩',
|
||||
"l.long": '⟵',
|
||||
"l.long.bar": '⟻',
|
||||
"l.long.squiggly": '⬳',
|
||||
"l.loop": '↫',
|
||||
"l.not": '↚',
|
||||
"l.quad": '⭅',
|
||||
"l.squiggly": '⇜',
|
||||
"l.stop": '⇤',
|
||||
"l.stroked": '⇦',
|
||||
"l.tail": '↢',
|
||||
"l.triple": '⇚',
|
||||
"l.twohead.bar": '⬶',
|
||||
"l.twohead": '↞',
|
||||
"l.wave": '↜',
|
||||
"t": '↑',
|
||||
"t.bar": '↥',
|
||||
"t.curve": '⤴',
|
||||
"t.dashed": '⇡',
|
||||
"t.double": '⇑',
|
||||
"t.filled": '⬆',
|
||||
"t.quad": '⟰',
|
||||
"t.stop": '⤒',
|
||||
"t.stroked": '⇧',
|
||||
"t.triple": '⤊',
|
||||
"t.twohead": '↟',
|
||||
"b": '↓',
|
||||
"b.bar": '↧',
|
||||
"b.curve": '⤵',
|
||||
"b.dashed": '⇣',
|
||||
"b.double": '⇓',
|
||||
"b.filled": '⬇',
|
||||
"b.quad": '⟱',
|
||||
"b.stop": '⤓',
|
||||
"b.stroked": '⇩',
|
||||
"b.triple": '⤋',
|
||||
"b.twohead": '↡',
|
||||
"l.r": '↔',
|
||||
"l.r.double": '⇔',
|
||||
"l.r.double.long": '⟺',
|
||||
"l.r.double.not": '⇎',
|
||||
"l.r.filled": '⬌',
|
||||
"l.r.long": '⟷',
|
||||
"l.r.not": '↮',
|
||||
"l.r.stroked": '⬄',
|
||||
"l.r.wave": '↭',
|
||||
"t.b": '↕',
|
||||
"t.b.double": '⇕',
|
||||
"t.b.filled": '⬍',
|
||||
"t.b.stroked": '⇳',
|
||||
"tr": '↗',
|
||||
"tr.double": '⇗',
|
||||
"tr.filled": '⬈',
|
||||
"tr.hook": '⤤',
|
||||
"tr.stroked": '⬀',
|
||||
"br": '↘',
|
||||
"br.double": '⇘',
|
||||
"br.filled": '⬊',
|
||||
"br.hook": '⤥',
|
||||
"br.stroked": '⬂',
|
||||
"tl": '↖',
|
||||
"tl.double": '⇖',
|
||||
"tl.filled": '⬉',
|
||||
"tl.hook": '⤣',
|
||||
"tl.stroked": '⬁',
|
||||
"bl": '↙',
|
||||
"bl.double": '⇙',
|
||||
"bl.filled": '⬋',
|
||||
"bl.hook": '⤦',
|
||||
"bl.stroked": '⬃',
|
||||
"tl.br": '⤡',
|
||||
"tr.bl": '⤢',
|
||||
"ccw": '↺',
|
||||
"ccw.half": '↶',
|
||||
"cw": '↻',
|
||||
"cw.half": '↷',
|
||||
"zigzag": '↯',
|
||||
],
|
||||
arrows: [
|
||||
"rr": '⇉',
|
||||
"ll": '⇇',
|
||||
"tt": '⇈',
|
||||
"bb": '⇊',
|
||||
"lr": '⇆',
|
||||
"lr.stop": '↹',
|
||||
"rl": '⇄',
|
||||
"tb": '⇅',
|
||||
"bt": '⇵',
|
||||
"rrr": '⇶',
|
||||
"lll": '⬱',
|
||||
],
|
||||
arrowhead: [
|
||||
"t": '⌃',
|
||||
"b": '⌄',
|
||||
],
|
||||
harpoon: [
|
||||
"rt": '⇀',
|
||||
"rt.bar": '⥛',
|
||||
"rt.stop": '⥓',
|
||||
"rb": '⇁',
|
||||
"rb.bar": '⥟',
|
||||
"rb.stop": '⥗',
|
||||
"lt": '↼',
|
||||
"lt.bar": '⥚',
|
||||
"lt.stop": '⥒',
|
||||
"lb": '↽',
|
||||
"lb.bar": '⥞',
|
||||
"lb.stop": '⥖',
|
||||
"tl": '↿',
|
||||
"tl.bar": '⥠',
|
||||
"tl.stop": '⥘',
|
||||
"tr": '↾',
|
||||
"tr.bar": '⥜',
|
||||
"tr.stop": '⥔',
|
||||
"bl": '⇃',
|
||||
"bl.bar": '⥡',
|
||||
"bl.stop": '⥙',
|
||||
"br": '⇂',
|
||||
"br.bar": '⥝',
|
||||
"br.stop": '⥕',
|
||||
"lt.rt": '⥎',
|
||||
"lb.rb": '⥐',
|
||||
"lb.rt": '⥋',
|
||||
"lt.rb": '⥊',
|
||||
"tl.bl": '⥑',
|
||||
"tr.br": '⥏',
|
||||
"tl.br": '⥍',
|
||||
"tr.bl": '⥌',
|
||||
],
|
||||
harpoons: [
|
||||
"rtrb": '⥤',
|
||||
"blbr": '⥥',
|
||||
"bltr": '⥯',
|
||||
"lbrb": '⥧',
|
||||
"ltlb": '⥢',
|
||||
"ltrb": '⇋',
|
||||
"ltrt": '⥦',
|
||||
"rblb": '⥩',
|
||||
"rtlb": '⇌',
|
||||
"rtlt": '⥨',
|
||||
"tlbr": '⥮',
|
||||
"tltr": '⥣',
|
||||
],
|
||||
}
|
@ -43,8 +43,6 @@ pub struct LangItems {
|
||||
pub text_id: NodeId,
|
||||
/// Get the string if this is a text node.
|
||||
pub text_str: fn(&Content) -> Option<&str>,
|
||||
/// Symbol notation: `:arrow:l:`.
|
||||
pub symbol: fn(notation: EcoString) -> Content,
|
||||
/// A smart quote: `'` or `"`.
|
||||
pub smart_quote: fn(double: bool) -> Content,
|
||||
/// A paragraph break.
|
||||
|
@ -23,6 +23,7 @@ mod module;
|
||||
mod ops;
|
||||
mod realize;
|
||||
mod scope;
|
||||
mod symbol;
|
||||
mod typeset;
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -42,5 +43,6 @@ pub use self::realize::*;
|
||||
pub use self::scope::*;
|
||||
pub use self::str::*;
|
||||
pub use self::styles::*;
|
||||
pub use self::symbol::*;
|
||||
pub use self::typeset::*;
|
||||
pub use self::value::*;
|
||||
|
@ -108,6 +108,15 @@ impl Scope {
|
||||
self.0.insert(var.into(), Slot::new(value.into(), Kind::Captured));
|
||||
}
|
||||
|
||||
/// Copy definitions from another scope that aren't yet defined in this one.
|
||||
pub fn copy_from(&mut self, other: &Self) {
|
||||
for (name, value) in other.iter() {
|
||||
self.0
|
||||
.entry(name.clone())
|
||||
.or_insert_with(|| Slot::new(value.clone(), Kind::Normal));
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to access a variable immutably.
|
||||
pub fn get(&self, var: &str) -> Option<&Value> {
|
||||
self.0.get(var).map(Slot::read)
|
||||
|
152
src/model/symbol.rs
Normal file
152
src/model/symbol.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use std::cmp::Reverse;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
|
||||
use crate::diag::StrResult;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Define a list of symbols.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __symbols {
|
||||
($func:ident, $($name:ident: $value:tt),* $(,)?) => {
|
||||
pub(super) fn $func(scope: &mut $crate::model::Scope) {
|
||||
$(scope.define(stringify!($name), $crate::model::symbols!(@one $value));)*
|
||||
}
|
||||
};
|
||||
(@one $c:literal) => { $crate::model::Symbol::new($c) };
|
||||
(@one [$($first:literal $(: $second:literal)?),* $(,)?]) => {
|
||||
$crate::model::Symbol::list(&[
|
||||
$($crate::model::symbols!(@pair $first $(: $second)?)),*
|
||||
])
|
||||
};
|
||||
(@pair $first:literal) => { ("", $first) };
|
||||
(@pair $first:literal: $second:literal) => { ($first, $second) };
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::__symbols as symbols;
|
||||
|
||||
/// A symbol.
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Symbol {
|
||||
repr: Repr,
|
||||
modifiers: EcoString,
|
||||
}
|
||||
|
||||
/// A collection of symbols.
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
enum Repr {
|
||||
Single(char),
|
||||
List(&'static [(&'static str, char)]),
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
/// Create a new symbol from a single character.
|
||||
pub fn new(c: char) -> Self {
|
||||
Self { repr: Repr::Single(c), modifiers: EcoString::new() }
|
||||
}
|
||||
|
||||
/// Create a symbol with variants.
|
||||
#[track_caller]
|
||||
pub fn list(list: &'static [(&'static str, char)]) -> Self {
|
||||
debug_assert!(!list.is_empty());
|
||||
Self {
|
||||
repr: Repr::List(list),
|
||||
modifiers: EcoString::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the symbol's text.
|
||||
pub fn get(&self) -> char {
|
||||
match self.repr {
|
||||
Repr::Single(c) => c,
|
||||
Repr::List(list) => find(list, &self.modifiers).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a modifier to the symbol.
|
||||
pub fn modified(mut self, modifier: &str) -> StrResult<Self> {
|
||||
if !self.modifiers.is_empty() {
|
||||
self.modifiers.push('.');
|
||||
}
|
||||
self.modifiers.push_str(modifier);
|
||||
if match self.repr {
|
||||
Repr::Single(_) => true,
|
||||
Repr::List(list) => find(list, &self.modifiers).is_none(),
|
||||
} {
|
||||
Err("unknown modifier")?
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// The characters that are covered by this symbol.
|
||||
pub fn chars(&self) -> impl Iterator<Item = char> {
|
||||
let (first, slice) = match self.repr {
|
||||
Repr::Single(c) => (Some(c), [].as_slice()),
|
||||
Repr::List(list) => (None, list),
|
||||
};
|
||||
first.into_iter().chain(slice.iter().map(|&(_, c)| c))
|
||||
}
|
||||
|
||||
/// Possible modifiers.
|
||||
pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
|
||||
let mut set = BTreeSet::new();
|
||||
if let Repr::List(list) = self.repr {
|
||||
for modifier in list.iter().flat_map(|(name, _)| name.split('.')) {
|
||||
if !modifier.is_empty() && !contained(&self.modifiers, modifier) {
|
||||
set.insert(modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
set.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Symbol {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_char(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the best symbol from the list.
|
||||
fn find(list: &[(&str, char)], modifiers: &str) -> Option<char> {
|
||||
let mut best = None;
|
||||
let mut best_score = None;
|
||||
|
||||
// Find the best table entry with this name.
|
||||
'outer: for candidate in list {
|
||||
for modifier in parts(modifiers) {
|
||||
if !contained(candidate.0, modifier) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
let mut matching = 0;
|
||||
let mut total = 0;
|
||||
for modifier in parts(candidate.0) {
|
||||
if contained(modifiers, modifier) {
|
||||
matching += 1;
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
let score = (matching, Reverse(total));
|
||||
if best_score.map_or(true, |b| score > b) {
|
||||
best = Some(candidate.1);
|
||||
best_score = Some(score);
|
||||
}
|
||||
}
|
||||
|
||||
best
|
||||
}
|
||||
|
||||
/// Split a modifier list into its parts.
|
||||
fn parts(modifiers: &str) -> impl Iterator<Item = &str> {
|
||||
modifiers.split('.').filter(|s| !s.is_empty())
|
||||
}
|
||||
|
||||
/// Whether the modifier string contains the modifier `m`.
|
||||
fn contained(modifiers: &str, m: &str) -> bool {
|
||||
parts(modifiers).any(|part| part == m)
|
||||
}
|
@ -7,10 +7,12 @@ use std::sync::Arc;
|
||||
use siphasher::sip128::{Hasher128, SipHasher};
|
||||
|
||||
use super::{
|
||||
format_str, ops, Args, Array, Cast, CastInfo, Content, Dict, Func, Label, Module, Str,
|
||||
format_str, ops, Args, Array, Cast, CastInfo, Content, Dict, Func, Label, Module,
|
||||
Str, Symbol,
|
||||
};
|
||||
use crate::diag::StrResult;
|
||||
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// A computational value.
|
||||
@ -38,6 +40,8 @@ pub enum Value {
|
||||
Fraction(Fr),
|
||||
/// A color value: `#f79143ff`.
|
||||
Color(Color),
|
||||
/// A symbol: `arrow.l`.
|
||||
Symbol(Symbol),
|
||||
/// A string: `"string"`.
|
||||
Str(Str),
|
||||
/// A label: `<intro>`.
|
||||
@ -81,6 +85,7 @@ impl Value {
|
||||
Self::Relative(_) => Rel::<Length>::TYPE_NAME,
|
||||
Self::Fraction(_) => Fr::TYPE_NAME,
|
||||
Self::Color(_) => Color::TYPE_NAME,
|
||||
Self::Symbol(_) => Symbol::TYPE_NAME,
|
||||
Self::Str(_) => Str::TYPE_NAME,
|
||||
Self::Label(_) => Label::TYPE_NAME,
|
||||
Self::Content(_) => Content::TYPE_NAME,
|
||||
@ -98,11 +103,33 @@ impl Value {
|
||||
T::cast(self)
|
||||
}
|
||||
|
||||
/// Try to access a field on the value.
|
||||
pub fn field(&self, field: &str) -> StrResult<Value> {
|
||||
match self {
|
||||
Self::Symbol(symbol) => symbol.clone().modified(&field).map(Self::Symbol),
|
||||
Self::Dict(dict) => dict.at(&field).cloned(),
|
||||
Self::Content(content) => content
|
||||
.field(&field)
|
||||
.ok_or_else(|| format_eco!("unknown field `{field}`")),
|
||||
Self::Module(module) => module.get(&field).cloned(),
|
||||
v => Err(format_eco!("cannot access fields on type {}", v.type_name())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the debug representation of the value.
|
||||
pub fn repr(&self) -> Str {
|
||||
format_str!("{:?}", self)
|
||||
}
|
||||
|
||||
/// Attach a span to the value, if possibly.
|
||||
pub fn spanned(self, span: Span) -> Self {
|
||||
match self {
|
||||
Value::Content(v) => Value::Content(v.spanned(span)),
|
||||
Value::Func(v) => Value::Func(v.spanned(span)),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the display representation of the value.
|
||||
pub fn display(self) -> Content {
|
||||
match self {
|
||||
@ -110,6 +137,7 @@ impl Value {
|
||||
Self::Int(v) => item!(text)(format_eco!("{}", v)),
|
||||
Self::Float(v) => item!(text)(format_eco!("{}", v)),
|
||||
Self::Str(v) => item!(text)(v.into()),
|
||||
Self::Symbol(v) => item!(text)(v.get().into()),
|
||||
Self::Content(v) => v,
|
||||
Self::Func(_) => Content::empty(),
|
||||
Self::Module(module) => module.content(),
|
||||
@ -122,6 +150,8 @@ impl Value {
|
||||
match self {
|
||||
Self::Int(v) => item!(math_atom)(format_eco!("{}", v)),
|
||||
Self::Float(v) => item!(math_atom)(format_eco!("{}", v)),
|
||||
Self::Symbol(v) => item!(math_atom)(v.get().into()),
|
||||
Self::Str(v) => item!(math_atom)(v.into()),
|
||||
_ => self.display(),
|
||||
}
|
||||
}
|
||||
@ -147,6 +177,7 @@ impl Debug for Value {
|
||||
Self::Relative(v) => Debug::fmt(v, f),
|
||||
Self::Fraction(v) => Debug::fmt(v, f),
|
||||
Self::Color(v) => Debug::fmt(v, f),
|
||||
Self::Symbol(v) => Debug::fmt(v, f),
|
||||
Self::Str(v) => Debug::fmt(v, f),
|
||||
Self::Label(v) => Debug::fmt(v, f),
|
||||
Self::Content(_) => f.pad("[...]"),
|
||||
@ -187,6 +218,7 @@ impl Hash for Value {
|
||||
Self::Relative(v) => v.hash(state),
|
||||
Self::Fraction(v) => v.hash(state),
|
||||
Self::Color(v) => v.hash(state),
|
||||
Self::Symbol(v) => v.hash(state),
|
||||
Self::Str(v) => v.hash(state),
|
||||
Self::Label(v) => v.hash(state),
|
||||
Self::Content(v) => v.hash(state),
|
||||
@ -398,11 +430,13 @@ primitive! { Rel<Length>: "relative length",
|
||||
}
|
||||
primitive! { Fr: "fraction", Fraction }
|
||||
primitive! { Color: "color", Color }
|
||||
primitive! { Symbol: "symbol", Symbol }
|
||||
primitive! { Str: "string", Str }
|
||||
primitive! { Label: "label", Label }
|
||||
primitive! { Content: "content",
|
||||
Content,
|
||||
None => Content::empty(),
|
||||
Symbol(symbol) => item!(text)(symbol.get().into()),
|
||||
Str(text) => item!(text)(text.into())
|
||||
}
|
||||
primitive! { Array: "array", Array }
|
||||
|
@ -89,8 +89,6 @@ pub enum Expr {
|
||||
/// A shorthand for a unicode codepoint. For example, `~` for non-breaking
|
||||
/// space or `-?` for a soft hyphen.
|
||||
Shorthand(Shorthand),
|
||||
/// Symbol notation: `:arrow:l:`.
|
||||
Symbol(Symbol),
|
||||
/// A smart quote: `'` or `"`.
|
||||
SmartQuote(SmartQuote),
|
||||
/// Strong content: `*Strong*`.
|
||||
@ -413,18 +411,6 @@ impl Shorthand {
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// Symbol notation: `:arrow:l:`.
|
||||
Symbol
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
/// Get the symbol's notation.
|
||||
pub fn get(&self) -> &str {
|
||||
self.0.text().trim_matches(':')
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// A smart quote: `'` or `"`.
|
||||
SmartQuote
|
||||
|
@ -24,9 +24,6 @@ pub enum SyntaxKind {
|
||||
/// A shorthand for a unicode codepoint. For example, `~` for non-breaking
|
||||
/// space or `-?` for a soft hyphen.
|
||||
Shorthand,
|
||||
/// Symbol notation: `:arrow:l:`. The string only contains the inner part
|
||||
/// without leading and trailing dot.
|
||||
Symbol,
|
||||
/// A smart quote: `'` or `"`.
|
||||
SmartQuote,
|
||||
/// Strong content: `*Strong*`.
|
||||
@ -332,7 +329,6 @@ impl SyntaxKind {
|
||||
Self::Parbreak => "paragraph break",
|
||||
Self::Escape => "escape sequence",
|
||||
Self::Shorthand => "shorthand",
|
||||
Self::Symbol => "symbol notation",
|
||||
Self::Strong => "strong content",
|
||||
Self::Emph => "emphasized content",
|
||||
Self::Raw => "raw block",
|
||||
|
@ -167,21 +167,12 @@ impl Lexer<'_> {
|
||||
fn markup(&mut self, start: usize, c: char) -> SyntaxKind {
|
||||
match c {
|
||||
'\\' => self.backslash(),
|
||||
':' if self.s.at(is_id_start) => self.maybe_symbol(),
|
||||
'`' => self.raw(),
|
||||
'h' if self.s.eat_if("ttp://") => self.link(),
|
||||
'h' if self.s.eat_if("ttps://") => self.link(),
|
||||
'0'..='9' => self.numbering(start),
|
||||
'<' if self.s.at(is_id_continue) => self.label(),
|
||||
'@' if self.s.at(is_id_continue) => self.reference(),
|
||||
'#' if self.s.eat_if('{') => SyntaxKind::LeftBrace,
|
||||
'#' if self.s.eat_if('[') => SyntaxKind::LeftBracket,
|
||||
'#' if self.s.at(is_id_start) => {
|
||||
match keyword(self.s.eat_while(is_id_continue)) {
|
||||
Some(keyword) => keyword,
|
||||
None => SyntaxKind::Ident,
|
||||
}
|
||||
}
|
||||
|
||||
'.' if self.s.eat_if("..") => SyntaxKind::Shorthand,
|
||||
'-' if self.s.eat_if("--") => SyntaxKind::Shorthand,
|
||||
@ -190,8 +181,7 @@ impl Lexer<'_> {
|
||||
'*' if !self.in_word() => SyntaxKind::Star,
|
||||
'_' if !self.in_word() => SyntaxKind::Underscore,
|
||||
|
||||
'{' => SyntaxKind::LeftBrace,
|
||||
'}' => SyntaxKind::RightBrace,
|
||||
'#' if !self.s.at(char::is_whitespace) => SyntaxKind::Hashtag,
|
||||
'[' => SyntaxKind::LeftBracket,
|
||||
']' => SyntaxKind::RightBracket,
|
||||
'\'' => SyntaxKind::SmartQuote,
|
||||
@ -241,26 +231,6 @@ impl Lexer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_symbol(&mut self) -> SyntaxKind {
|
||||
let start = self.s.cursor();
|
||||
let mut end = start;
|
||||
while !self.s.eat_while(is_id_continue).is_empty() && self.s.at(':') {
|
||||
end = self.s.cursor();
|
||||
self.s.eat();
|
||||
}
|
||||
|
||||
self.s.jump(end);
|
||||
|
||||
if start < end {
|
||||
self.s.expect(':');
|
||||
SyntaxKind::Symbol
|
||||
} else if self.mode == LexMode::Markup {
|
||||
SyntaxKind::Colon
|
||||
} else {
|
||||
SyntaxKind::Atom
|
||||
}
|
||||
}
|
||||
|
||||
fn raw(&mut self) -> SyntaxKind {
|
||||
let mut backticks = 1;
|
||||
while self.s.eat_if('`') {
|
||||
@ -408,7 +378,6 @@ impl Lexer<'_> {
|
||||
fn math(&mut self, start: usize, c: char) -> SyntaxKind {
|
||||
match c {
|
||||
'\\' => self.backslash(),
|
||||
':' if self.s.at(is_id_start) => self.maybe_symbol(),
|
||||
'"' => self.string(),
|
||||
|
||||
'.' if self.s.eat_if("..") => SyntaxKind::Shorthand,
|
||||
@ -434,9 +403,10 @@ impl Lexer<'_> {
|
||||
'^' => SyntaxKind::Hat,
|
||||
'&' => SyntaxKind::MathAlignPoint,
|
||||
|
||||
// Identifiers and symbol notation.
|
||||
// Identifiers.
|
||||
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {
|
||||
self.math_ident()
|
||||
self.s.eat_while(is_math_id_continue);
|
||||
SyntaxKind::MathIdent
|
||||
}
|
||||
|
||||
// Other math atoms.
|
||||
@ -444,25 +414,6 @@ impl Lexer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn math_ident(&mut self) -> SyntaxKind {
|
||||
self.s.eat_while(is_math_id_continue);
|
||||
|
||||
let mut symbol = false;
|
||||
while self.s.eat_if(':') && !self.s.eat_while(char::is_alphanumeric).is_empty() {
|
||||
symbol = true;
|
||||
}
|
||||
|
||||
if symbol {
|
||||
return SyntaxKind::Symbol;
|
||||
}
|
||||
|
||||
if self.s.scout(-1) == Some(':') {
|
||||
self.s.uneat();
|
||||
}
|
||||
|
||||
SyntaxKind::Ident
|
||||
}
|
||||
|
||||
fn atom(&mut self, start: usize, c: char) -> SyntaxKind {
|
||||
// Keep numbers and grapheme clusters together.
|
||||
if c.is_numeric() {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.3 KiB |
@ -23,6 +23,6 @@ $ accent(a, grave),
|
||||
accent(a, overline),
|
||||
accent(a, breve),
|
||||
accent(a, dot),
|
||||
accent(a, dia),
|
||||
accent(a, diaer),
|
||||
accent(a, caron),
|
||||
accent(a, arrow) $
|
||||
|
@ -2,7 +2,7 @@
|
||||
#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)))
|
||||
|
||||
#let cells = ([:triangle:nested:], [--], [`italic`], [`bold`], [both])
|
||||
#let cells = (sym.triangle.nested, [--], [`italic`], [`bold`], [both])
|
||||
#for kk in kinds {
|
||||
cells.push(raw(repr(kk).trim("<function ").trim(">")))
|
||||
for mm in modifiers {
|
||||
|
@ -19,6 +19,6 @@ $ sqrt(x^2) = frac(x, 1) $
|
||||
$ "profit" = "income" - "expenses" $
|
||||
$ x < #for i in range(5) [$ #i < $] y $
|
||||
$ 1 + 2 = #{1 + 2} $
|
||||
$ A sub:eq:not B $
|
||||
$ A subset.eq.not B $
|
||||
```
|
||||
<table>
|
||||
|
@ -1,27 +1,18 @@
|
||||
// Test symbol notation.
|
||||
// Test symbols.
|
||||
|
||||
---
|
||||
:face:
|
||||
:face:unknown:
|
||||
:woman:old:
|
||||
:turtle:
|
||||
#emoji.face
|
||||
#emoji.woman.old
|
||||
#emoji.turtle
|
||||
|
||||
#set text("New Computer Modern Math")
|
||||
:arrow:
|
||||
:arrow:l:
|
||||
:arrow:r:squiggly:
|
||||
#symbol(("arrow", "tr", "hook").join(":"))
|
||||
#sym.arrow
|
||||
#sym.arrow.l
|
||||
#sym.arrow.r.squiggly
|
||||
#sym.arrow.tr.hook
|
||||
|
||||
#sym.arrow.r;this and this#sym.arrow.l;
|
||||
|
||||
---
|
||||
Just a: colon. \
|
||||
Still :not a symbol. \
|
||||
Also not:a symbol \
|
||||
:arrow:r:this and this:arrow:l: \
|
||||
|
||||
---
|
||||
#show symbol.where(notation: "my:custom"): "MY"
|
||||
This is :my:custom: notation.
|
||||
|
||||
---
|
||||
// Error: 1-14 unknown symbol
|
||||
:nonexisting:
|
||||
// Error: 13-20 unknown modifier
|
||||
#emoji.face.garbage
|
||||
|
Loading…
x
Reference in New Issue
Block a user