diff options
| author | Malo <57839069+MDLC01@users.noreply.github.com> | 2025-06-26 09:24:21 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-26 08:24:21 +0000 |
| commit | 04fd0acacab8cf2e82268da9c18ef4bcf37507dc (patch) | |
| tree | d01aa881ebdab6f3925af65c18d71b1103836c90 /crates/typst-library/src/foundations | |
| parent | 6a1d6c08e2d6e4c184c6d177e67796b23ccbe4c7 (diff) | |
Allow deprecating symbol variants (#6441)
Diffstat (limited to 'crates/typst-library/src/foundations')
| -rw-r--r-- | crates/typst-library/src/foundations/symbol.rs | 59 | ||||
| -rw-r--r-- | crates/typst-library/src/foundations/value.rs | 4 |
2 files changed, 41 insertions, 22 deletions
diff --git a/crates/typst-library/src/foundations/symbol.rs b/crates/typst-library/src/foundations/symbol.rs index 0f503edd..f57bb0c2 100644 --- a/crates/typst-library/src/foundations/symbol.rs +++ b/crates/typst-library/src/foundations/symbol.rs @@ -8,7 +8,7 @@ use serde::{Serialize, Serializer}; use typst_syntax::{is_ident, Span, Spanned}; use typst_utils::hash128; -use crate::diag::{bail, SourceResult, StrResult}; +use crate::diag::{bail, DeprecationSink, SourceResult, StrResult}; use crate::foundations::{ cast, elem, func, scope, ty, Array, Content, Func, NativeElement, NativeFunc, Packed, PlainText, Repr as _, @@ -54,18 +54,22 @@ enum Repr { /// A native symbol that has no named variant. Single(char), /// A native symbol with multiple named variants. - Complex(&'static [(ModifierSet<&'static str>, char)]), + Complex(&'static [Variant<&'static str>]), /// A symbol with multiple named variants, where some modifiers may have /// been applied. Also used for symbols defined at runtime by the user with /// no modifier applied. Modified(Arc<(List, ModifierSet<EcoString>)>), } +/// A symbol variant, consisting of a set of modifiers, a character, and an +/// optional deprecation message. +type Variant<S> = (ModifierSet<S>, char, Option<S>); + /// A collection of symbols. #[derive(Clone, Eq, PartialEq, Hash)] enum List { - Static(&'static [(ModifierSet<&'static str>, char)]), - Runtime(Box<[(ModifierSet<EcoString>, char)]>), + Static(&'static [Variant<&'static str>]), + Runtime(Box<[Variant<EcoString>]>), } impl Symbol { @@ -76,14 +80,14 @@ impl Symbol { /// Create a symbol with a static variant list. #[track_caller] - pub const fn list(list: &'static [(ModifierSet<&'static str>, char)]) -> Self { + pub const fn list(list: &'static [Variant<&'static str>]) -> Self { debug_assert!(!list.is_empty()); Self(Repr::Complex(list)) } /// Create a symbol with a runtime variant list. #[track_caller] - pub fn runtime(list: Box<[(ModifierSet<EcoString>, char)]>) -> Self { + pub fn runtime(list: Box<[Variant<EcoString>]>) -> Self { debug_assert!(!list.is_empty()); Self(Repr::Modified(Arc::new((List::Runtime(list), ModifierSet::default())))) } @@ -93,9 +97,11 @@ impl Symbol { match &self.0 { Repr::Single(c) => *c, Repr::Complex(_) => ModifierSet::<&'static str>::default() - .best_match_in(self.variants()) + .best_match_in(self.variants().map(|(m, c, _)| (m, c))) .unwrap(), - Repr::Modified(arc) => arc.1.best_match_in(self.variants()).unwrap(), + Repr::Modified(arc) => { + arc.1.best_match_in(self.variants().map(|(m, c, _)| (m, c))).unwrap() + } } } @@ -128,7 +134,11 @@ impl Symbol { } /// Apply a modifier to the symbol. - pub fn modified(mut self, modifier: &str) -> StrResult<Self> { + pub fn modified( + mut self, + sink: impl DeprecationSink, + modifier: &str, + ) -> StrResult<Self> { if let Repr::Complex(list) = self.0 { self.0 = Repr::Modified(Arc::new((List::Static(list), ModifierSet::default()))); @@ -137,7 +147,12 @@ impl Symbol { if let Repr::Modified(arc) = &mut self.0 { let (list, modifiers) = Arc::make_mut(arc); modifiers.insert_raw(modifier); - if modifiers.best_match_in(list.variants()).is_some() { + if let Some(deprecation) = + modifiers.best_match_in(list.variants().map(|(m, _, d)| (m, d))) + { + if let Some(message) = deprecation { + sink.emit(message) + } return Ok(self); } } @@ -146,7 +161,7 @@ impl Symbol { } /// The characters that are covered by this symbol. - pub fn variants(&self) -> impl Iterator<Item = (ModifierSet<&str>, char)> { + pub fn variants(&self) -> impl Iterator<Item = Variant<&str>> { match &self.0 { Repr::Single(c) => Variants::Single(Some(*c).into_iter()), Repr::Complex(list) => Variants::Static(list.iter()), @@ -161,7 +176,7 @@ impl Symbol { _ => ModifierSet::default(), }; self.variants() - .flat_map(|(m, _)| m) + .flat_map(|(m, _, _)| m) .filter(|modifier| !modifier.is_empty() && !modifiers.contains(modifier)) .collect::<BTreeSet<_>>() .into_iter() @@ -256,7 +271,7 @@ impl Symbol { let list = variants .into_iter() - .map(|s| (ModifierSet::from_raw_dotted(s.v.0), s.v.1)) + .map(|s| (ModifierSet::from_raw_dotted(s.v.0), s.v.1, None)) .collect(); Ok(Symbol::runtime(list)) } @@ -316,17 +331,17 @@ impl crate::foundations::Repr for Symbol { } fn repr_variants<'a>( - variants: impl Iterator<Item = (ModifierSet<&'a str>, char)>, + variants: impl Iterator<Item = Variant<&'a str>>, applied_modifiers: ModifierSet<&str>, ) -> String { crate::foundations::repr::pretty_array_like( &variants - .filter(|(modifiers, _)| { + .filter(|(modifiers, _, _)| { // Only keep variants that can still be accessed, i.e., variants // that contain all applied modifiers. applied_modifiers.iter().all(|am| modifiers.contains(am)) }) - .map(|(modifiers, c)| { + .map(|(modifiers, c, _)| { let trimmed_modifiers = modifiers.into_iter().filter(|&m| !applied_modifiers.contains(m)); if trimmed_modifiers.clone().all(|m| m.is_empty()) { @@ -379,18 +394,20 @@ cast! { /// Iterator over variants. enum Variants<'a> { Single(std::option::IntoIter<char>), - Static(std::slice::Iter<'static, (ModifierSet<&'static str>, char)>), - Runtime(std::slice::Iter<'a, (ModifierSet<EcoString>, char)>), + Static(std::slice::Iter<'static, Variant<&'static str>>), + Runtime(std::slice::Iter<'a, Variant<EcoString>>), } impl<'a> Iterator for Variants<'a> { - type Item = (ModifierSet<&'a str>, char); + type Item = Variant<&'a str>; fn next(&mut self) -> Option<Self::Item> { match self { - Self::Single(iter) => Some((ModifierSet::default(), iter.next()?)), + Self::Single(iter) => Some((ModifierSet::default(), iter.next()?, None)), Self::Static(list) => list.next().copied(), - Self::Runtime(list) => list.next().map(|(m, c)| (m.as_deref(), *c)), + Self::Runtime(list) => { + list.next().map(|(m, c, d)| (m.as_deref(), *c, d.as_deref())) + } } } } diff --git a/crates/typst-library/src/foundations/value.rs b/crates/typst-library/src/foundations/value.rs index 854c2486..4bcf2d4e 100644 --- a/crates/typst-library/src/foundations/value.rs +++ b/crates/typst-library/src/foundations/value.rs @@ -157,7 +157,9 @@ impl Value { /// Try to access a field on the value. pub fn field(&self, field: &str, sink: impl DeprecationSink) -> StrResult<Value> { match self { - Self::Symbol(symbol) => symbol.clone().modified(field).map(Self::Symbol), + Self::Symbol(symbol) => { + symbol.clone().modified(sink, field).map(Self::Symbol) + } Self::Version(version) => version.component(field).map(Self::Int), Self::Dict(dict) => dict.get(field).cloned(), Self::Content(content) => content.field_by_name(field), |
