Allow deprecating symbol variants (#6441)

This commit is contained in:
Malo 2025-06-26 09:24:21 +01:00 committed by GitHub
parent 6a1d6c08e2
commit 04fd0acaca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 38 deletions

2
Cargo.lock generated
View File

@ -413,7 +413,7 @@ dependencies = [
[[package]]
name = "codex"
version = "0.1.1"
source = "git+https://github.com/typst/codex?rev=56eb217#56eb2172fc0670f4c1c8b79a63d11f9354e5babe"
source = "git+https://github.com/typst/codex?rev=a5428cb#a5428cb9c81a41354d44b44dbd5a16a710bbd928"
[[package]]
name = "color-print"

View File

@ -47,7 +47,7 @@ clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
clap_complete = "4.2.1"
clap_mangen = "0.2.10"
codespan-reporting = "0.11"
codex = { git = "https://github.com/typst/codex", rev = "56eb217" }
codex = { git = "https://github.com/typst/codex", rev = "a5428cb" }
color-print = "0.3.6"
comemo = "0.4"
csv = "1"

View File

@ -448,7 +448,7 @@ fn field_access_completions(
match value {
Value::Symbol(symbol) => {
for modifier in symbol.modifiers() {
if let Ok(modified) = symbol.clone().modified(modifier) {
if let Ok(modified) = symbol.clone().modified((), modifier) {
ctx.completions.push(Completion {
kind: CompletionKind::Symbol(modified.get()),
label: modifier.into(),

View File

@ -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()))
}
}
}
}

View File

@ -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),

View File

@ -720,18 +720,12 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
}
};
for (variant, c) in symbol.variants() {
for (variant, c, deprecation) in symbol.variants() {
let shorthand = |list: &[(&'static str, char)]| {
list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s)
};
let name = complete(variant);
let deprecation = match name.as_str() {
"integral.sect" => {
Some("`integral.sect` is deprecated, use `integral.inter` instead")
}
_ => binding.deprecation(),
};
list.push(SymbolModel {
name,
@ -742,10 +736,10 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
accent: typst::math::Accent::combine(c).is_some(),
alternates: symbol
.variants()
.filter(|(other, _)| other != &variant)
.map(|(other, _)| complete(other))
.filter(|(other, _, _)| other != &variant)
.map(|(other, _, _)| complete(other))
.collect(),
deprecation,
deprecation: deprecation.or_else(|| binding.deprecation()),
});
}
}

View File

@ -121,8 +121,8 @@ $a scripts(=)^"def" b quad a scripts(lt.eq)_"really" b quad a scripts(arrow.r.lo
--- math-attach-integral ---
// Test default of scripts attachments on integrals at display size.
$ integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b $
$integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b$
$ integral.inter_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b $
$integral.inter_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b$
--- math-attach-large-operator ---
// Test default of limit attachments on large operators at display size only.