diff --git a/Cargo.lock b/Cargo.lock index 69b01b2ff..4674b425d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index ac16e1bad..4c178a2ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/library/Cargo.toml b/library/Cargo.toml index b7930432d..d0debe61b 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -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" diff --git a/library/src/lib.rs b/library/src/lib.rs index bdb2cc907..76245b9e0 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -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"); global.def_func::("linebreak"); - global.def_func::("symbol"); global.def_func::("smartquote"); global.def_func::("strong"); global.def_func::("emph"); @@ -49,6 +49,8 @@ fn global(math: Module) -> Module { global.def_func::("strike"); global.def_func::("overline"); global.def_func::("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_str: |content| Some(&content.to::()?.0), - symbol: |notation| text::SymbolNode(notation).pack(), smart_quote: |double| text::SmartQuoteNode { double }.pack(), parbreak: || layout::ParbreakNode.pack(), strong: |body| text::StrongNode(body).pack(), diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index cbe636b1b..1e7a65807 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -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::("formula"); math.def_func::("lr"); @@ -77,7 +78,9 @@ pub fn module() -> Module { math.def_func::("mono"); math.def_func::("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::() { - 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::() { for child in &node.0 { child.layout_math(ctx)?; diff --git a/library/src/math/symbols.rs b/library/src/math/symbols.rs new file mode 100644 index 000000000..34330d473 --- /dev/null +++ b/library/src/math/symbols.rs @@ -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: '⌀', +} diff --git a/library/src/text/emoji.rs b/library/src/text/emoji.rs new file mode 100644 index 000000000..f38f27383 --- /dev/null +++ b/library/src/text/emoji.rs @@ -0,0 +1,1355 @@ +use typst::model::{symbols, Module, Scope}; + +/// A module with all emoji. +pub fn emoji() -> Module { + let mut scope = Scope::new(); + define(&mut scope); + Module::new("emoji").with_scope(scope) +} + +symbols! { + define, + + abacus: '🧮', + abc: '🔤', + abcd: '🔡', + ABCD: '🔠', + accordion: '🪗', + aesculapius: '⚕', + airplane: [ + '✈', + "landing": '🛬', + "small": '🛩', + "takeoff": '🛫', + ], + alembic: '⚗', + alien: ['👽', "monster": '👾'], + ambulance: '🚑', + amphora: '🏺', + anchor: '⚓', + anger: '💢', + ant: '🐜', + apple: ["green": '🍏', "red": '🍎'], + arm: ["mech": '🦾', "muscle": '💪', "selfie": '🤳'], + arrow: [ + "r.filled": '➡', + "r.hook": '↪', + "r.soon": '🔜', + "l.filled": '⬅', + "l.hook": '↩', + "l.back": '🔙', + "l.end": '🔚', + "t.filled": '⬆', + "t.curve": '⤴', + "t.top": '🔝', + "b.filled": '⬇', + "b.curve": '⤵', + "l.r": '↔', + "l.r.on": '🔛', + "t.b": '↕', + "bl": '↙', + "br": '↘', + "tl": '↖', + "tr": '↗', + ], + arrows: ["cycle": '🔄'], + ast: ['*', "box": '✳'], + atm: '🏧', + atom: '⚛', + aubergine: '🍆', + avocado: '🥑', + axe: '🪓', + baby: ['👶', "angel": '👼', "box": '🚼'], + babybottle: '🍼', + backpack: '🎒', + bacon: '🥓', + badger: '🦡', + badminton: '🏸', + bagel: '🥯', + baggageclaim: '🛄', + baguette: '🥖', + balloon: '🎈', + ballot: ["check": '☑'], + ballotbox: '🗳', + banana: '🍌', + banjo: '🪕', + bank: '🏦', + barberpole: '💈', + baseball: '⚾', + basecap: '🧢', + basket: '🧺', + basketball: ['⛹', "ball": '🏀'], + bat: '🦇', + bathtub: ['🛀', "foam": '🛁'], + battery: ['🔋', "low": '🪫'], + beach: ["palm": '🏝', "umbrella": '🏖'], + beads: '📿', + beans: '🫘', + bear: '🐻', + beaver: '🦫', + bed: ['🛏', "person": '🛌'], + bee: '🐝', + beer: ['🍺', "clink": '🍻'], + beetle: ['🪲', "lady": '🐞'], + bell: ['🔔', "ding": '🛎', "not": '🔕'], + bento: '🍱', + bicyclist: ['🚴', "mountain": '🚵'], + bike: ['🚲', "not": '🚳'], + bikini: '👙', + billiards: '🎱', + bin: '🗑', + biohazard: '☣', + bird: '🐦', + bison: '🦬', + blood: '🩸', + blouse: '👚', + blowfish: '🐡', + blueberries: '🫐', + boar: '🐗', + boat: [ + "sail": '⛵', + "row": '🚣', + "motor": '🛥', + "speed": '🚤', + "canoe": '🛶', + ], + bolt: '🔩', + bomb: '💣', + bone: '🦴', + book: [ + "red": '📕', + "blue": '📘', + "green": '📗', + "orange": '📙', + "spiral": '📒', + "open": '📖', + ], + bookmark: '🔖', + books: '📚', + boomerang: '🪃', + bordercontrol: '🛂', + bouquet: '💐', + bow: '🏹', + bowl: ["spoon": '🥣', "steam": '🍜'], + bowling: '🎳', + boxing: '🥊', + boy: '👦', + brain: '🧠', + bread: '🍞', + brick: '🧱', + bride: '👰', + bridge: ["fog": '🌁', "night": '🌉'], + briefcase: '💼', + briefs: '🩲', + brightness: ["high": '🔆', "low": '🔅'], + broccoli: '🥦', + broom: '🧹', + brush: '🖌', + bubble: [ + "speech.r": '💬', + "speech.l": '🗨', + "thought": '💭', + "anger.r": '🗯', + ], + bubbles: '🫧', + bubbletea: '🧋', + bucket: '🪣', + buffalo: ["water": '🐃'], + bug: '🐛', + builder: '👷', + burger: '🍔', + burrito: '🌯', + bus: [ + '🚌', + "front": '🚍', + "small": '🚐', + "stop": '🚏', + "trolley": '🚎', + ], + butter: '🧈', + butterfly: '🦋', + button: ['🔲', "alt": '🔳', "radio": '🔘'], + cabinet: ["file": '🗄'], + cablecar: ['🚠', "small": '🚡'], + cactus: '🌵', + cake: [ + '🎂', + "fish": '🍥', + "moon": '🥮', + "slice": '🍰', + ], + calendar: ['📅', "spiral": '🗓', "tearoff": '📆'], + camel: ['🐫', "dromedar": '🐪'], + camera: [ + '📷', + "flash": '📸', + "movie": '🎥', + "movie.box": '🎦', + "video": '📹', + ], + camping: '🏕', + can: '🥫', + candle: '🕯', + candy: '🍬', + cane: '🦯', + car: [ + '🚗', + "front": '🚘', + "pickup": '🛻', + "police": '🚓', + "police.front": '🚔', + "racing": '🏎', + "rickshaw": '🛺', + "suv": '🚙', + ], + card: ["credit": '💳', "id": '🪪'], + cardindex: '📇', + carrot: '🥕', + cart: '🛒', + cassette: '📼', + castle: ["eu": '🏰', "jp": '🏯'], + cat: [ + '🐈', + "face": '🐱', + "face.angry": '😾', + "face.cry": '😿', + "face.heart": '😻', + "face.joy": '😹', + "face.kiss": '😽', + "face.laugh": '😸', + "face.shock": '🙀', + "face.smile": '😺', + "face.smirk": '😼', + ], + chain: '🔗', + chains: '⛓', + chair: '🪑', + champagne: '🍾', + chart: [ + "bar": '📊', + "up": '📈', + "down": '📉', + "yen.up": '💹', + ], + checkmark: ["heavy": '✔', "box": '✅'], + cheese: '🧀', + cherries: '🍒', + chess: '♟', + chestnut: '🌰', + chicken: [ + '🐔', + "baby": '🐥', + "baby.egg": '🐣', + "baby.head": '🐤', + "leg": '🍗', + "male": '🐓', + ], + child: '🧒', + chipmunk: '🐿', + chocolate: '🍫', + chopsticks: '🥢', + church: ['⛪', "love": '💒'], + cigarette: ['🚬', "not": '🚭'], + circle: [ + "black": '⚫', + "blue": '🔵', + "brown": '🟤', + "green": '🟢', + "orange": '🟠', + "purple": '🟣', + "white": '⚪', + "red": '🔴', + "yellow": '🟡', + "stroked": '⭕', + ], + circus: '🎪', + city: [ + '🏙', + "dusk": '🌆', + "night": '🌃', + "sunset": '🌇', + ], + clamp: '🗜', + clapperboard: '🎬', + climbing: '🧗', + clip: '📎', + clipboard: '📋', + clips: '🖇', + clock: [ + "one": '🕐', + "one.thirty": '🕜', + "two": '🕑', + "two.thirty": '🕝', + "three": '🕒', + "three.thirty": '🕞', + "four": '🕓', + "four.thirty": '🕟', + "five": '🕔', + "five.thirty": '🕠', + "six": '🕕', + "six.thirty": '🕡', + "seven": '🕖', + "seven.thirty": '🕢', + "eight": '🕗', + "eight.thirty": '🕣', + "nine": '🕘', + "nine.thirty": '🕤', + "ten": '🕙', + "ten.thirty": '🕥', + "eleven": '🕚', + "eleven.thirty": '🕦', + "twelve": '🕛', + "twelve.thirty": '🕧', + "alarm": '⏰', + "old": '🕰', + "timer": '⏲', + ], + cloud: [ + '☁', + "dust": '💨', + "rain": '🌧', + "snow": '🌨', + "storm": '⛈', + "sun": '⛅', + "sun.hidden": '🌥', + "sun.rain": '🌦', + "thunder": '🌩', + ], + coat: ['🧥', "lab": '🥼'], + cockroach: '🪳', + cocktail: ["martini": '🍸', "tropical": '🍹'], + coconut: '🥥', + coffee: '☕', + coffin: '⚰', + coin: '🪙', + comet: '☄', + compass: '🧭', + computer: '🖥', + computermouse: '🖱', + confetti: '🎊', + construction: '🚧', + controller: '🎮', + cookie: ['🍪', "fortune": '🥠'], + cooking: '🍳', + cool: '🆒', + copyright: '©', + coral: '🪸', + corn: '🌽', + couch: '🛋', + couple: '💑', + cow: ['🐄', "face": '🐮'], + crab: '🦀', + crane: '🏗', + crayon: '🖍', + cricket: '🦗', + cricketbat: '🏏', + crocodile: '🐊', + croissant: '🥐', + crossmark: ['❌', "box": '❎'], + crown: '👑', + crutch: '🩼', + crystal: '🔮', + cucumber: '🥒', + cup: ["straw": '🥤'], + cupcake: '🧁', + curling: '🥌', + curry: '🍛', + custard: '🍮', + customs: '🛃', + cutlery: '🍴', + cyclone: '🌀', + dancing: ["man": '🕺', "woman": '💃', "women.bunny": '👯'], + darts: '🎯', + dash: ["wave.double": '〰'], + deer: '🦌', + desert: '🏜', + detective: '🕵', + diamond: [ + "blue": '🔷', + "blue.small": '🔹', + "orange": '🔶', + "orange.small": '🔸', + "dot": '💠', + ], + die: '🎲', + dino: ["pod": '🦕', "rex": '🦖'], + disc: ["cd": '💿', "dvd": '📀', "mini": '💽'], + discoball: '🪩', + diving: '🤿', + dodo: '🦤', + dog: [ + '🐕', + "face": '🐶', + "guide": '🦮', + "poodle": '🐩', + ], + dollar: '💲', + dolphin: '🐬', + donut: '🍩', + door: '🚪', + dove: ["peace": '🕊'], + dragon: ['🐉', "face": '🐲'], + dress: ['👗', "kimono": '👘', "sari": '🥻'], + drop: '💧', + drops: '💦', + drum: ['🥁', "big": '🪘'], + duck: '🦆', + dumpling: '🥟', + eagle: '🦅', + ear: ['👂', "aid": '🦻'], + egg: '🥚', + eighteen: ["not": '🔞'], + elephant: '🐘', + elevator: '🛗', + elf: '🧝', + email: '📧', + excl: [ + '❗', + "white": '❕', + "double": '‼', + "quest": '⁉', + ], + explosion: '💥', + extinguisher: '🧯', + eye: '👁', + eyes: '👀', + face: [ + "grin": '😀', + "angry": '😠', + "angry.red": '😡', + "anguish": '😧', + "astonish": '😲', + "bandage": '🤕', + "beam": '😁', + "blank": '😶', + "clown": '🤡', + "cold": '🥶', + "concern": '😦', + "cool": '😎', + "cover": '🤭', + "cowboy": '🤠', + "cry": '😭', + "devil.smile": '😈', + "devil.frown": '👿', + "diagonal": '🫤', + "disguise": '🥸', + "dizzy": '😵', + "dotted": '🫥', + "down": '😞', + "down.sweat": '😓', + "drool": '🤤', + "explode": '🤯', + "eyeroll": '🙄', + "friendly": '☺', + "fear": '😨', + "fear.sweat": '😰', + "fever": '🤒', + "flush": '😳', + "frown": '☹', + "frown.slight": '🙁', + "frust": '😣', + "goofy": '🤪', + "halo": '😇', + "happy": '😊', + "heart": '😍', + "hearts": '🥰', + "heat": '🥵', + "hug": '🤗', + "inv": '🙃', + "joy": '😂', + "kiss": '😗', + "kiss.smile": '😙', + "kiss.heart": '😘', + "kiss.blush": '😚', + "lick": '😋', + "lie": '🤥', + "mask": '😷', + "meh": '😒', + "melt": '🫠', + "money": '🤑', + "monocle": '🧐', + "nausea": '🤢', + "nerd": '🤓', + "neutral": '😐', + "open": '😃', + "party": '🥳', + "peek": '🫣', + "plead": '🥺', + "relief": '😌', + "rofl": '🤣', + "sad": '😔', + "salute": '🫡', + "shock": '😱', + "shush": '🤫', + "skeptic": '🤨', + "sleep": '😴', + "sleepy": '😪', + "smile": '😄', + "smile.slight": '🙂', + "smile.sweat": '😅', + "smile.tear": '🥲', + "smirk": '😏', + "sneeze": '🤧', + "speak.not": '🫢', + "squint": '😆', + "stars": '🤩', + "straight": '😑', + "suffer": '😖', + "surprise": '😯', + "symbols": '🤬', + "tear": '😢', + "tear.relief": '😥', + "tear.withheld": '🥹', + "teeth": '😬', + "think": '🤔', + "tired": '😫', + "tongue": '😛', + "tongue.squint": '😝', + "tongue.wink": '😜', + "triumph": '😤', + "unhappy": '😕', + "vomit": '🤮', + "weary": '😩', + "wink": '😉', + "woozy": '🥴', + "worry": '😟', + "wow": '😮', + "yawn": '🥱', + "zip": '🤐', + ], + factory: '🏭', + fairy: '🧚', + faith: [ + "christ": '✝', + "dharma": '☸', + "islam": '☪', + "judaism": '✡', + "menorah": '🕎', + "om": '🕉', + "orthodox": '☦', + "peace": '☮', + "star.dot": '🔯', + "worship": '🛐', + "yinyang": '☯', + ], + falafel: '🧆', + family: '👪', + fax: '📠', + feather: '🪶', + feeding: ["breast": '🤱'], + fencing: '🤺', + ferriswheel: '🎡', + filebox: '🗃', + filedividers: '🗂', + film: '🎞', + finger: [ + "r": '👉', + "l": '👈', + "t": '👆', + "t.alt": '☝', + "b": '👇', + "front": '🫵', + "m": '🖕', + ], + fingers: ["cross": '🤞', "pinch": '🤌', "snap": '🫰'], + fire: '🔥', + firecracker: '🧨', + fireengine: '🚒', + fireworks: '🎆', + fish: ['🐟', "tropical": '🐠'], + fishing: '🎣', + fist: [ + "front": '👊', + "r": '🤜', + "l": '🤛', + "raised": '✊', + ], + flag: [ + "black": '🏴', + "white": '🏳', + "goal": '🏁', + "golf": '⛳', + "red": '🚩', + ], + flags: ["jp.crossed": '🎌'], + flamingo: '🦩', + flashlight: '🔦', + flatbread: '🫓', + fleur: '⚜', + floppy: '💾', + flower: [ + "hibiscus": '🌺', + "lotus": '🪷', + "pink": '🌸', + "rose": '🌹', + "sun": '🌻', + "tulip": '🌷', + "white": '💮', + "wilted": '🥀', + "yellow": '🌼', + ], + fly: '🪰', + fog: '🌫', + folder: ['📁', "open": '📂'], + fondue: '🫕', + foot: '🦶', + football: ['⚽', "am": '🏈'], + forex: '💱', + fountain: '⛲', + fox: '🦊', + free: '🆓', + fries: '🍟', + frisbee: '🥏', + frog: ["face": '🐸'], + fuelpump: '⛽', + garlic: '🧄', + gear: '⚙', + gem: '💎', + genie: '🧞', + ghost: '👻', + giraffe: '🦒', + girl: '👧', + glass: [ + "clink": '🥂', + "milk": '🥛', + "pour": '🫗', + "tumbler": '🥃', + ], + glasses: ['👓', "sun": '🕶'], + globe: [ + "am": '🌎', + "as.au": '🌏', + "eu.af": '🌍', + "meridian": '🌐', + ], + gloves: '🧤', + goal: '🥅', + goat: '🐐', + goggles: '🥽', + golfing: '🏌', + gorilla: '🦍', + grapes: '🍇', + guard: ["man": '💂'], + guitar: '🎸', + gymnastics: '🤸', + haircut: '💇', + hammer: ['🔨', "pick": '⚒', "wrench": '🛠'], + hamsa: '🪬', + hamster: ["face": '🐹'], + hand: [ + "raised": '✋', + "raised.alt": '🤚', + "r": '🫱', + "l": '🫲', + "t": '🫴', + "b": '🫳', + "ok": '👌', + "call": '🤙', + "love": '🤟', + "part": '🖖', + "peace": '✌', + "pinch": '🤏', + "rock": '🤘', + "splay": '🖐', + "wave": '👋', + "write": '✍', + ], + handbag: '👜', + handball: '🤾', + handholding: ["man.man": '👬', "woman.man": '👫', "woman.woman": '👭'], + hands: [ + "folded": '🙏', + "palms": '🤲', + "clap": '👏', + "heart": '🫶', + "open": '👐', + "raised": '🙌', + "shake": '🤝', + ], + hash: '#', + hat: ["ribbon": '👒', "top": '🎩'], + headphone: '🎧', + heart: [ + '❤', + "arrow": '💘', + "beat": '💓', + "black": '🖤', + "blue": '💙', + "box": '💟', + "broken": '💔', + "brown": '🤎', + "double": '💕', + "excl": '❣', + "green": '💚', + "grow": '💗', + "orange": '🧡', + "purple": '💜', + "real": '🫀', + "revolve": '💞', + "ribbon": '💝', + "spark": '💖', + "white": '🤍', + "yellow": '💛', + ], + hedgehog: '🦔', + helicopter: '🚁', + helix: '🧬', + helmet: ["cross": '⛑', "military": '🪖'], + hippo: '🦛', + hockey: '🏑', + hole: '🕳', + honey: '🍯', + hongbao: '🧧', + hook: '🪝', + horn: ["postal": '📯'], + horse: [ + '🐎', + "carousel": '🎠', + "face": '🐴', + "race": '🏇', + ], + hospital: '🏥', + hotdog: '🌭', + hotel: ['🏨', "love": '🏩'], + hotspring: '♨', + hourglass: ['⌛', "flow": '⏳'], + house: [ + '🏠', + "derelict": '🏚', + "garden": '🏡', + "multiple": '🏘', + ], + hundred: '💯', + hut: '🛖', + ice: '🧊', + icecream: ['🍨', "shaved": '🍧', "soft": '🍦'], + icehockey: '🏒', + id: '🆔', + info: 'ℹ', + izakaya: '🏮', + jar: '🫙', + jeans: '👖', + jigsaw: '🧩', + joystick: '🕹', + juggling: '🤹', + juice: '🧃', + kaaba: '🕋', + kadomatsu: '🎍', + kangaroo: '🦘', + gachi: '🈷', + go: '🈴', + hi: '㊙', + ka: '🉑', + kachi: '🈹', + kara: '🈳', + kon: '🈲', + man: '🈵', + muryo: '🈚', + shin: '🈸', + shuku: '㊗', + toku: '🉐', + yo: '🈺', + yubi: '🈯', + yuryo: '🈶', + koko: '🈁', + sa: '🈂', + kebab: '🥙', + key: ['🔑', "old": '🗝'], + keyboard: '⌨', + kiss: '💏', + kissmark: '💋', + kite: '🪁', + kiwi: '🥝', + knife: ['🔪', "dagger": '🗡'], + knot: '🪢', + koala: '🐨', + koinobori: '🎏', + label: '🏷', + lacrosse: '🥍', + ladder: '🪜', + lamp: ["diya": '🪔'], + laptop: '💻', + a: '🅰', + ab: '🆎', + b: '🅱', + cl: '🆑', + o: '🅾', + leaf: [ + "clover.three": '☘', + "clover.four": '🍀', + "fall": '🍂', + "herb": '🌿', + "maple": '🍁', + "wind": '🍃', + ], + leftluggage: '🛅', + leg: ['🦵', "mech": '🦿'], + lemon: '🍋', + leopard: '🐆', + letter: ["love": '💌'], + liberty: '🗽', + lightbulb: '💡', + lightning: '⚡', + lion: '🦁', + lipstick: '💄', + litter: ['🚮', "not": '🚯'], + lizard: '🦎', + llama: '🦙', + lobster: '🦞', + lock: [ + '🔒', + "key": '🔐', + "open": '🔓', + "pen": '🔏', + ], + lollipop: '🍭', + lotion: '🧴', + luggage: '🧳', + lungs: '🫁', + mage: '🧙', + magnet: '🧲', + magnify: ["r": '🔎', "l": '🔍'], + mahjong: ["dragon.red": '🀄'], + mail: ['✉', "arrow": '📩'], + mailbox: [ + "closed.empty": '📪', + "closed.full": '📫', + "open.empty": '📭', + "open.full": '📬', + ], + mammoth: '🦣', + man: [ + '👨', + "box": '🚹', + "crown": '🤴', + "guapimao": '👲', + "levitate": '🕴', + "old": '👴', + "pregnant": '🫃', + "turban": '👳', + "tuxedo": '🤵', + ], + mango: '🥭', + map: ["world": '🗺', "jp": '🗾'], + martialarts: '🥋', + masks: '🎭', + mate: '🧉', + matryoshka: '🪆', + meat: ['🥩', "bone": '🍖'], + medal: [ + "first": '🥇', + "second": '🥈', + "third": '🥉', + "sports": '🏅', + "military": '🎖', + ], + megaphone: ['📢', "simple": '📣'], + melon: '🍈', + merperson: '🧜', + metro: 'Ⓜ', + microbe: '🦠', + microphone: ['🎤', "studio": '🎙'], + microscope: '🔬', + milkyway: '🌌', + mirror: '🪞', + mixer: '🎛', + money: [ + "bag": '💰', + "dollar": '💵', + "euro": '💶', + "pound": '💷', + "yen": '💴', + "wings": '💸', + ], + monkey: [ + '🐒', + "face": '🐵', + "hear.not": '🙉', + "see.not": '🙈', + "speak.not": '🙊', + ], + moon: [ + "crescent": '🌙', + "full": '🌕', + "full.face": '🌝', + "new": '🌑', + "new.face": '🌚', + "wane.one": '🌖', + "wane.two": '🌗', + "wane.three.face": '🌜', + "wane.three": '🌘', + "wax.one": '🌒', + "wax.two": '🌓', + "wax.two.face": '🌛', + "wax.three": '🌔', + ], + mortarboard: '🎓', + mosque: '🕌', + mosquito: '🦟', + motorcycle: '🏍', + motorway: '🛣', + mountain: [ + '⛰', + "fuji": '🗻', + "snow": '🏔', + "sunrise": '🌄', + ], + mouse: ['🐁', "face": '🐭'], + mousetrap: '🪤', + mouth: ['👄', "bite": '🫦'], + moyai: '🗿', + museum: '🏛', + mushroom: '🍄', + musicalscore: '🎼', + nails: ["polish": '💅'], + namebadge: '📛', + nazar: '🧿', + necktie: '👔', + needle: '🪡', + nest: ["empty": '🪹', "eggs": '🪺'], + new: '🆕', + newspaper: ['📰', "rolled": '🗞'], + ng: '🆖', + ningyo: '🎎', + ninja: '🥷', + noentry: '⛔', + nose: '👃', + notebook: ['📓', "deco": '📔'], + notepad: '🗒', + notes: ['🎵', "triple": '🎶'], + numbers: '🔢', + octopus: '🐙', + office: '🏢', + oil: '🛢', + ok: '🆗', + olive: '🫒', + oni: '👹', + onion: '🧅', + orangutan: '🦧', + otter: '🦦', + owl: '🦉', + ox: '🐂', + oyster: '🦪', + package: '📦', + paella: '🥘', + page: ['📄', "curl": '📃', "pencil": '📝'], + pager: '📟', + pages: ["tabs": '📑'], + painting: '🖼', + palette: '🎨', + pancakes: '🥞', + panda: '🐼', + parachute: '🪂', + park: '🏞', + parking: '🅿', + parrot: '🦜', + partalteration: '〽', + party: '🎉', + peach: '🍑', + peacock: '🦚', + peanuts: '🥜', + pear: '🍐', + pedestrian: ['🚶', "not": '🚷'], + pen: ["ball": '🖊', "fountain": '🖋'], + pencil: '✏', + penguin: '🐧', + pepper: ['🫑', "hot": '🌶'], + person: [ + '🧑', + "angry": '🙎', + "beard": '🧔', + "blonde": '👱', + "bow": '🙇', + "crown": '🫅', + "deaf": '🧏', + "facepalm": '🤦', + "frown": '🙍', + "hijab": '🧕', + "kneel": '🧎', + "lotus": '🧘', + "massage": '💆', + "no": '🙅', + "ok": '🙆', + "old": '🧓', + "pregnant": '🫄', + "raise": '🙋', + "sassy": '💁', + "shrug": '🤷', + "stand": '🧍', + "steam": '🧖', + ], + petri: '🧫', + phone: [ + '📱', + "arrow": '📲', + "classic": '☎', + "not": '📵', + "off": '📴', + "receiver": '📞', + "signal": '📶', + "vibrate": '📳', + ], + piano: '🎹', + pick: '⛏', + pie: '🥧', + pig: ['🐖', "face": '🐷', "node": '🐽'], + pill: '💊', + pin: ['📌', "round": '📍'], + pinata: '🪅', + pineapple: '🍍', + pingpong: '🏓', + pistol: '🔫', + pizza: '🍕', + placard: '🪧', + planet: '🪐', + plant: '🪴', + plaster: '🩹', + plate: ["cutlery": '🍽'], + playback: [ + "down": '⏬', + "eject": '⏏', + "forward": '⏩', + "pause": '⏸', + "record": '⏺', + "repeat": '🔁', + "repeat.once": '🔂', + "repeat.v": '🔃', + "restart": '⏮', + "rewind": '⏪', + "shuffle": '🔀', + "skip": '⏭', + "stop": '⏹', + "toggle": '⏯', + "up": '⏫', + ], + playingcard: ["flower": '🎴', "joker": '🃏'], + plunger: '🪠', + policeofficer: '👮', + poo: '💩', + popcorn: '🍿', + post: ["eu": '🏤', "jp": '🏣'], + postbox: '📮', + potato: ['🥔', "sweet": '🍠'], + pouch: '👝', + powerplug: '🔌', + present: '🎁', + pretzel: '🥨', + printer: '🖨', + prints: ["foot": '👣', "paw": '🐾'], + prohibited: '🚫', + projector: '📽', + pumpkin: ["lantern": '🎃'], + purse: '👛', + quest: ['❓', "white": '❔'], + rabbit: ['🐇', "face": '🐰'], + raccoon: '🦝', + radio: '📻', + radioactive: '☢', + railway: '🛤', + rainbow: '🌈', + ram: '🐏', + rat: '🐀', + razor: '🪒', + receipt: '🧾', + recycling: '♻', + reg: '®', + restroom: '🚻', + rhino: '🦏', + ribbon: ['🎀', "remind": '🎗'], + rice: [ + '🍚', + "cracker": '🍘', + "ear": '🌾', + "onigiri": '🍙', + ], + ring: '💍', + ringbuoy: '🛟', + robot: '🤖', + rock: '🪨', + rocket: '🚀', + rollercoaster: '🎢', + rosette: '🏵', + rugby: '🏉', + ruler: ['📏', "triangle": '📐'], + running: '🏃', + safetypin: '🧷', + safetyvest: '🦺', + sake: '🍶', + salad: '🥗', + salt: '🧂', + sandwich: '🥪', + santa: ["man": '🎅', "woman": '🤶'], + satdish: '📡', + satellite: '🛰', + saw: '🪚', + saxophone: '🎷', + scales: '⚖', + scarf: '🧣', + school: '🏫', + scissors: '✂', + scooter: ['🛴', "motor": '🛵'], + scorpion: '🦂', + screwdriver: '🪛', + scroll: '📜', + seal: '🦭', + seat: '💺', + seedling: '🌱', + shark: '🦈', + sheep: '🐑', + shell: ["spiral": '🐚'], + shield: '🛡', + ship: ['🚢', "cruise": '🛳', "ferry": '⛴'], + shirt: ["sports": '🎽', "t": '👕'], + shoe: [ + '👞', + "ballet": '🩰', + "flat": '🥿', + "heel": '👠', + "hike": '🥾', + "ice": '⛸', + "roller": '🛼', + "sandal.heel": '👡', + "ski": '🎿', + "sneaker": '👟', + "tall": '👢', + "thong": '🩴', + ], + shopping: '🛍', + shorts: '🩳', + shoshinsha: '🔰', + shower: '🚿', + shrimp: ['🦐', "fried": '🍤'], + shrine: '⛩', + sign: ["crossing": '🚸', "stop": '🛑'], + silhouette: [ + '👤', + "double": '👥', + "hug": '🫂', + "speak": '🗣', + ], + siren: '🚨', + skateboard: '🛹', + skewer: ["dango": '🍡', "oden": '🍢'], + skiing: '⛷', + skull: ['💀', "bones": '☠'], + skunk: '🦨', + sled: '🛷', + slide: '🛝', + slider: '🎚', + sloth: '🦥', + slots: '🎰', + snail: '🐌', + snake: '🐍', + snowboarding: '🏂', + snowflake: '❄', + snowman: ['⛄', "snow": '☃'], + soap: '🧼', + socks: '🧦', + softball: '🥎', + sos: '🆘', + soup: '🍲', + spaghetti: '🍝', + sparkle: ["box": '❇'], + sparkler: '🎇', + sparkles: '✨', + speaker: [ + '🔈', + "not": '🔇', + "wave": '🔉', + "waves": '🔊', + ], + spider: '🕷', + spiderweb: '🕸', + spinach: '🥬', + sponge: '🧽', + spoon: '🥄', + square: [ + "black": '⬛', + "black.tiny": '▪', + "black.small": '◾', + "black.medium": '◼', + "white": '⬜', + "white.tiny": '▫', + "white.small": '◽', + "white.medium": '◻', + "blue": '🟦', + "brown": '🟫', + "green": '🟩', + "orange": '🟧', + "purple": '🟪', + "red": '🟥', + "yellow": '🟨', + ], + squid: '🦑', + stadium: '🏟', + star: [ + '⭐', + "arc": '💫', + "box": '✴', + "glow": '🌟', + "shoot": '🌠', + ], + stethoscope: '🩺', + store: ["big": '🏬', "small": '🏪'], + strawberry: '🍓', + suit: [ + "club": '♣', + "diamond": '♦', + "heart": '♥', + "spade": '♠', + ], + sun: ['☀', "cloud": '🌤', "face": '🌞'], + sunrise: '🌅', + superhero: '🦸', + supervillain: '🦹', + surfing: '🏄', + sushi: '🍣', + swan: '🦢', + swimming: '🏊', + swimsuit: '🩱', + swords: '⚔', + symbols: '🔣', + synagogue: '🕍', + syringe: '💉', + taco: '🌮', + takeout: '🥡', + tamale: '🫔', + tanabata: '🎋', + tangerine: '🍊', + tap: ['🚰', "not": '🚱'], + taxi: ['🚕', "front": '🚖'], + teacup: '🍵', + teapot: '🫖', + teddy: '🧸', + telescope: '🔭', + temple: '🛕', + ten: '🔟', + tengu: '👺', + tennis: '🎾', + tent: '⛺', + testtube: '🧪', + thermometer: '🌡', + thread: '🧵', + thumb: ["up": '👍', "down": '👎'], + ticket: ["event": '🎟', "travel": '🎫'], + tiger: ['🐅', "face": '🐯'], + tm: '™', + toilet: '🚽', + toiletpaper: '🧻', + tomato: '🍅', + tombstone: '🪦', + tongue: '👅', + toolbox: '🧰', + tooth: '🦷', + toothbrush: '🪥', + tornado: '🌪', + tower: ["tokyo": '🗼'], + trackball: '🖲', + tractor: '🚜', + trafficlight: ["v": '🚦', "h": '🚥'], + train: [ + '🚆', + "car": '🚃', + "light": '🚈', + "metro": '🚇', + "mono": '🚝', + "mountain": '🚞', + "speed": '🚄', + "speed.bullet": '🚅', + "steam": '🚂', + "stop": '🚉', + "suspend": '🚟', + "tram": '🚊', + "tram.car": '🚋', + ], + transgender: '⚧', + tray: ["inbox": '📥', "mail": '📨', "outbox": '📤'], + tree: [ + "deciduous": '🌳', + "evergreen": '🌲', + "palm": '🌴', + "xmas": '🎄', + ], + triangle: [ + "r": '▶', + "l": '◀', + "t": '🔼', + "b": '🔽', + "t.red": '🔺', + "b.red": '🔻', + ], + trident: '🔱', + troll: '🧌', + trophy: '🏆', + truck: ['🚚', "trailer": '🚛'], + trumpet: '🎺', + tsukimi: '🎑', + turkey: '🦃', + turtle: '🐢', + tv: '📺', + ufo: '🛸', + umbrella: [ + "open": '☂', + "closed": '🌂', + "rain": '☔', + "sun": '⛱', + ], + unicorn: '🦄', + unknown: '🦳', + up: '🆙', + urn: '⚱', + vampire: '🧛', + violin: '🎻', + volcano: '🌋', + volleyball: '🏐', + vs: '🆚', + waffle: '🧇', + wand: '🪄', + warning: '⚠', + watch: ['⌚', "stop": '⏱'], + watermelon: '🍉', + waterpolo: '🤽', + wave: '🌊', + wc: '🚾', + weightlifting: '🏋', + whale: ['🐋', "spout": '🐳'], + wheel: '🛞', + wheelchair: ['🦽', "box": '♿', "motor": '🦼'], + wind: '🌬', + windchime: '🎐', + window: '🪟', + wine: '🍷', + wolf: '🐺', + woman: [ + '👩', + "box": '🚺', + "crown": '👸', + "old": '👵', + "pregnant": '🤰', + ], + wood: '🪵', + worm: '🪱', + wrench: '🔧', + wrestling: '🤼', + xray: '🩻', + yarn: '🧶', + yoyo: '🪀', + zebra: '🦓', + zodiac: [ + "aquarius": '♒', + "aries": '♈', + "cancer": '♋', + "capri": '♑', + "gemini": '♊', + "leo": '♌', + "libra": '♎', + "ophi": '⛎', + "pisces": '♓', + "sagit": '♐', + "scorpio": '♏', + "taurus": '♉', + "virgo": '♍', + ], + zombie: '🧟', + zzz: '💤', +} diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 3424cce6e..85e58ecd4 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -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; diff --git a/library/src/text/symbol.rs b/library/src/text/symbol.rs deleted file mode 100644 index 4a7c8ad32..000000000 --- a/library/src/text/symbol.rs +++ /dev/null @@ -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` | |-> | `↦` | -/// | `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 { - Ok(Self(args.expect("notation")?).pack()) - } - - fn field(&self, name: &str) -> Option { - match name { - "notation" => Some(Value::Str(self.0.clone().into())), - _ => None, - } - } -} - -impl Show for SymbolNode { - fn show(&self, _: &mut Vt, this: &Content, _: StyleChain) -> SourceResult { - 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()) - } - } - } -} diff --git a/library/src/text/symbols.rs b/library/src/text/symbols.rs new file mode 100644 index 000000000..36e49849e --- /dev/null +++ b/library/src/text/symbols.rs @@ -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": '⥣', + ], +} diff --git a/src/model/library.rs b/src/model/library.rs index cd9db10f2..773342b3c 100644 --- a/src/model/library.rs +++ b/src/model/library.rs @@ -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. diff --git a/src/model/mod.rs b/src/model/mod.rs index d84fe4642..d96a314c5 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -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::*; diff --git a/src/model/scope.rs b/src/model/scope.rs index bb0d46849..40307cba3 100644 --- a/src/model/scope.rs +++ b/src/model/scope.rs @@ -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) diff --git a/src/model/symbol.rs b/src/model/symbol.rs new file mode 100644 index 000000000..ac1d2b100 --- /dev/null +++ b/src/model/symbol.rs @@ -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 { + 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 { + 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 + '_ { + 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 { + 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 { + 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) +} diff --git a/src/model/value.rs b/src/model/value.rs index 8103b2119..15656c424 100644 --- a/src/model/value.rs +++ b/src/model/value.rs @@ -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: ``. @@ -81,6 +85,7 @@ impl Value { Self::Relative(_) => Rel::::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 { + 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: "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 } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index b91867874..3b573f7d8 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -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 diff --git a/src/syntax/kind.rs b/src/syntax/kind.rs index 34e2fce7e..f0a0bc5a0 100644 --- a/src/syntax/kind.rs +++ b/src/syntax/kind.rs @@ -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", diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index 0735270b5..d4548b8b6 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -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() { diff --git a/tests/ref/math/accents.png b/tests/ref/math/accents.png index 7e93d9b5e..dba9fb453 100644 Binary files a/tests/ref/math/accents.png and b/tests/ref/math/accents.png differ diff --git a/tests/ref/text/symbol.png b/tests/ref/text/symbol.png index 4806683b8..f964d9315 100644 Binary files a/tests/ref/text/symbol.png and b/tests/ref/text/symbol.png differ diff --git a/tests/typ/math/accents.typ b/tests/typ/math/accents.typ index 96d055e98..0cd0b16e5 100644 --- a/tests/typ/math/accents.typ +++ b/tests/typ/math/accents.typ @@ -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) $ diff --git a/tests/typ/math/style.typ b/tests/typ/math/style.typ index 5089610b1..4a4cfe160 100644 --- a/tests/typ/math/style.typ +++ b/tests/typ/math/style.typ @@ -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(""))) for mm in modifiers { diff --git a/tests/typ/math/syntax.typ b/tests/typ/math/syntax.typ index 186082aa5..f18f7edf5 100644 --- a/tests/typ/math/syntax.typ +++ b/tests/typ/math/syntax.typ @@ -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 $ ``` diff --git a/tests/typ/text/symbol.typ b/tests/typ/text/symbol.typ index f758c7793..3b7028e9b 100644 --- a/tests/typ/text/symbol.typ +++ b/tests/typ/text/symbol.typ @@ -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