Basic SymbolElem addition

This commit is contained in:
Ian Wrzesinski 2024-07-29 00:25:03 -05:00
parent 0b8b7d0f23
commit c47b71b435
8 changed files with 73 additions and 23 deletions

View File

@ -17,7 +17,9 @@ use rustybuzz::Feature;
use ttf_parser::Tag;
use typst_library::diag::{bail, SourceResult};
use typst_library::engine::Engine;
use typst_library::foundations::{Content, NativeElement, Packed, Resolve, StyleChain};
use typst_library::foundations::{
Content, NativeElement, Packed, Resolve, StyleChain, SymbolElem,
};
use typst_library::introspection::{Counter, Locator, SplitLocator, TagElem};
use typst_library::layout::{
Abs, AlignElem, Axes, BlockElem, BoxElem, Em, FixedAlignment, Fragment, Frame, HElem,
@ -535,6 +537,12 @@ fn layout_realized(
layout_h(elem, ctx, styles)?;
} else if let Some(elem) = elem.to_packed::<TextElem>() {
self::text::layout_text(elem, ctx, styles)?;
} else if let Some(elem) = elem.to_packed::<SymbolElem>() {
// This is a hack to avoid affecting layout that will be replaced in a
// later commit.
let text_elem = TextElem::new(elem.text.to_string().into());
let packed = Packed::new(text_elem);
self::text::layout_text(&packed, ctx, styles)?;
} else if let Some(elem) = elem.to_packed::<BoxElem>() {
layout_box(elem, ctx, styles)?;
} else if elem.is::<AlignPointElem>() {

View File

@ -6,7 +6,9 @@ use ecow::eco_format;
use typst_utils::Numeric;
use crate::diag::{bail, HintedStrResult, StrResult};
use crate::foundations::{format_str, Datetime, IntoValue, Regex, Repr, Value};
use crate::foundations::{
format_str, Datetime, IntoValue, Regex, Repr, SymbolElem, Value,
};
use crate::layout::{Alignment, Length, Rel};
use crate::text::TextElem;
use crate::visualize::Stroke;
@ -30,10 +32,10 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
(Symbol(a), Str(b)) => Str(format_str!("{a}{b}")),
(Bytes(a), Bytes(b)) => Bytes(a + b),
(Content(a), Content(b)) => Content(a + b),
(Content(a), Symbol(b)) => Content(a + TextElem::packed(b.get())),
(Content(a), Symbol(b)) => Content(a + SymbolElem::packed(b.get())),
(Content(a), Str(b)) => Content(a + TextElem::packed(b)),
(Str(a), Content(b)) => Content(TextElem::packed(a) + b),
(Symbol(a), Content(b)) => Content(TextElem::packed(a.get()) + b),
(Symbol(a), Content(b)) => Content(SymbolElem::packed(a.get()) + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),
(Args(a), Args(b)) => Args(a + b),
@ -130,10 +132,10 @@ pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
(Symbol(a), Str(b)) => Str(format_str!("{a}{b}")),
(Bytes(a), Bytes(b)) => Bytes(a + b),
(Content(a), Content(b)) => Content(a + b),
(Content(a), Symbol(b)) => Content(a + TextElem::packed(b.get())),
(Content(a), Symbol(b)) => Content(a + SymbolElem::packed(b.get())),
(Content(a), Str(b)) => Content(a + TextElem::packed(b)),
(Str(a), Content(b)) => Content(TextElem::packed(a) + b),
(Symbol(a), Content(b)) => Content(TextElem::packed(a.get()) + b),
(Symbol(a), Content(b)) => Content(SymbolElem::packed(a.get()) + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),

View File

@ -9,7 +9,10 @@ use typst_syntax::{is_ident, Span, Spanned};
use typst_utils::hash128;
use crate::diag::{bail, SourceResult, StrResult};
use crate::foundations::{cast, func, scope, ty, Array, Func, NativeFunc, Repr as _};
use crate::foundations::{
cast, elem, func, scope, ty, Array, Content, Func, NativeElement, NativeFunc, Packed,
PlainText, Repr as _,
};
/// A Unicode symbol.
///
@ -425,3 +428,31 @@ fn parts(modifiers: &str) -> impl Iterator<Item = &str> {
fn contained(modifiers: &str, m: &str) -> bool {
parts(modifiers).any(|part| part == m)
}
/// A single character.
#[elem(Repr, PlainText)]
pub struct SymbolElem {
/// The symbol's character.
#[required]
pub text: char, // This is called `text` for consistency with `TextElem`.
}
impl SymbolElem {
/// Create a new packed symbol element.
pub fn packed(text: impl Into<char>) -> Content {
Self::new(text.into()).pack()
}
}
impl PlainText for Packed<SymbolElem> {
fn plain_text(&self, text: &mut EcoString) {
text.push(self.text);
}
}
impl crate::foundations::Repr for SymbolElem {
/// Use a custom repr that matches normal content.
fn repr(&self) -> EcoString {
eco_format!("[{}]", self.text)
}
}

View File

@ -16,7 +16,7 @@ use crate::foundations::{
fields, ops, repr, Args, Array, AutoValue, Bytes, CastInfo, Content, Datetime,
Decimal, Dict, Duration, Fold, FromValue, Func, IntoValue, Label, Module,
NativeElement, NativeType, NoneValue, Plugin, Reflect, Repr, Resolve, Scope, Str,
Styles, Symbol, Type, Version,
Styles, Symbol, SymbolElem, Type, Version,
};
use crate::layout::{Abs, Angle, Em, Fr, Length, Ratio, Rel};
use crate::text::{RawContent, RawElem, TextElem};
@ -209,7 +209,7 @@ impl Value {
Self::Decimal(v) => TextElem::packed(eco_format!("{v}")),
Self::Str(v) => TextElem::packed(v),
Self::Version(v) => TextElem::packed(eco_format!("{v}")),
Self::Symbol(v) => TextElem::packed(v.get()),
Self::Symbol(v) => SymbolElem::packed(v.get()),
Self::Content(v) => v,
Self::Module(module) => module.content(),
_ => RawElem::new(RawContent::Text(self.repr()))
@ -656,7 +656,7 @@ primitive! { Duration: "duration", Duration }
primitive! { Content: "content",
Content,
None => Content::empty(),
Symbol(v) => TextElem::packed(v.get()),
Symbol(v) => SymbolElem::packed(v.get()),
Str(v) => TextElem::packed(v)
}
primitive! { Styles: "styles", Styles }

View File

@ -1,8 +1,7 @@
use crate::diag::bail;
use crate::foundations::{cast, elem, func, Content, NativeElement, Value};
use crate::foundations::{cast, elem, func, Content, NativeElement, SymbolElem};
use crate::layout::{Length, Rel};
use crate::math::Mathy;
use crate::text::TextElem;
/// Attaches an accent to a base.
///
@ -142,8 +141,8 @@ cast! {
Accent,
self => self.0.into_value(),
v: char => Self::new(v),
v: Content => match v.to_packed::<TextElem>() {
Some(elem) => Value::Str(elem.text.clone().into()).cast()?,
None => bail!("expected text"),
v: Content => match v.to_packed::<SymbolElem>() {
Some(elem) => Self::new(elem.text),
None => bail!("expected a symbol"),
},
}

View File

@ -16,7 +16,7 @@ use typst_library::engine::Engine;
use typst_library::foundations::{
Content, Context, ContextElem, Element, NativeElement, Recipe, RecipeIndex, Selector,
SequenceElem, Show, ShowSet, Style, StyleChain, StyleVec, StyledElem, Styles,
Synthesize, Transformation,
SymbolElem, Synthesize, Transformation,
};
use typst_library::html::{tag, HtmlElem};
use typst_library::introspection::{Locatable, SplitLocator, Tag, TagElem};
@ -221,7 +221,7 @@ impl<'a, 'x, 'y, 'z, 's> Grouped<'a, 'x, 'y, 'z, 's> {
/// Handles an arbitrary piece of content during realization.
fn visit<'a>(
s: &mut State<'a, '_, '_, '_>,
content: &'a Content,
mut content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<()> {
// Tags can always simply be pushed.
@ -230,6 +230,12 @@ fn visit<'a>(
return Ok(());
}
if let Some(elem) = content.to_packed::<SymbolElem>() {
// This is a hack to avoid affecting layout that will be replaced in a
// later commit.
content = Box::leak(Box::new(TextElem::packed(elem.text.to_string())));
}
// Transformations for math content based on the realization kind. Needs
// to happen before show rules.
if visit_math_rules(s, content, styles)? {

View File

@ -50,12 +50,14 @@
`raw` <myraw>
--- content-fields-complex ---
// Integrated test for content fields.
// Integrated test for content fields. The idea is to parse a normal looking
// equation and symbolically evaluate it with the given variable values.
#let compute(equation, ..vars) = {
let vars = vars.named()
let f(elem) = {
let func = elem.func()
if func == text {
if elem.has("text") {
let text = elem.text
if regex("^\d+$") in text {
int(text)
@ -74,7 +76,7 @@
elem
.children
.filter(v => v != [ ])
.split[+]
.split($+$.body)
.map(xs => xs.fold(1, (prod, v) => prod * f(v)))
.fold(0, (sum, v) => sum + v)
}
@ -83,13 +85,15 @@
[With ]
vars
.pairs()
.map(p => $#p.first() = #p.last()$)
.map(((name, value)) => $name = value$)
.join(", ", last: " and ")
[ we have:]
$ equation = result $
}
#compute($x y + y^2$, x: 2, y: 3)
// This should generate the same output as:
// With $x = 2$ and $y = 3$ we have: $ x y + y^2 = 15 $
--- content-label-has-method ---
// Test whether the label is accessible through the `has` method.

View File

@ -2,7 +2,7 @@
--- math-symbol-basic ---
#let sym = symbol("s", ("basic", "s"))
#test($sym.basic$, $#"s"$)
#test($sym.basic$, $s$)
--- math-symbol-underscore ---
#let sym = symbol("s", ("test_underscore", "s"))
@ -16,7 +16,7 @@ $sym.test-dash$
--- math-symbol-double ---
#let sym = symbol("s", ("test.basic", "s"))
#test($sym.test.basic$, $#"s"$)
#test($sym.test.basic$, $s$)
--- math-symbol-double-underscore ---
#let sym = symbol("s", ("one.test_underscore", "s"))