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]] [[package]]
name = "codex" name = "codex"
version = "0.1.1" 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]] [[package]]
name = "color-print" name = "color-print"

View File

@ -47,7 +47,7 @@ clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
clap_complete = "4.2.1" clap_complete = "4.2.1"
clap_mangen = "0.2.10" clap_mangen = "0.2.10"
codespan-reporting = "0.11" 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" color-print = "0.3.6"
comemo = "0.4" comemo = "0.4"
csv = "1" csv = "1"

View File

@ -448,7 +448,7 @@ fn field_access_completions(
match value { match value {
Value::Symbol(symbol) => { Value::Symbol(symbol) => {
for modifier in symbol.modifiers() { 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 { ctx.completions.push(Completion {
kind: CompletionKind::Symbol(modified.get()), kind: CompletionKind::Symbol(modified.get()),
label: modifier.into(), label: modifier.into(),

View File

@ -8,7 +8,7 @@ use serde::{Serialize, Serializer};
use typst_syntax::{is_ident, Span, Spanned}; use typst_syntax::{is_ident, Span, Spanned};
use typst_utils::hash128; use typst_utils::hash128;
use crate::diag::{bail, SourceResult, StrResult}; use crate::diag::{bail, DeprecationSink, SourceResult, StrResult};
use crate::foundations::{ use crate::foundations::{
cast, elem, func, scope, ty, Array, Content, Func, NativeElement, NativeFunc, Packed, cast, elem, func, scope, ty, Array, Content, Func, NativeElement, NativeFunc, Packed,
PlainText, Repr as _, PlainText, Repr as _,
@ -54,18 +54,22 @@ enum Repr {
/// A native symbol that has no named variant. /// A native symbol that has no named variant.
Single(char), Single(char),
/// A native symbol with multiple named variants. /// 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 /// A symbol with multiple named variants, where some modifiers may have
/// been applied. Also used for symbols defined at runtime by the user with /// been applied. Also used for symbols defined at runtime by the user with
/// no modifier applied. /// no modifier applied.
Modified(Arc<(List, ModifierSet<EcoString>)>), 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. /// A collection of symbols.
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
enum List { enum List {
Static(&'static [(ModifierSet<&'static str>, char)]), Static(&'static [Variant<&'static str>]),
Runtime(Box<[(ModifierSet<EcoString>, char)]>), Runtime(Box<[Variant<EcoString>]>),
} }
impl Symbol { impl Symbol {
@ -76,14 +80,14 @@ impl Symbol {
/// Create a symbol with a static variant list. /// Create a symbol with a static variant list.
#[track_caller] #[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()); debug_assert!(!list.is_empty());
Self(Repr::Complex(list)) Self(Repr::Complex(list))
} }
/// Create a symbol with a runtime variant list. /// Create a symbol with a runtime variant list.
#[track_caller] #[track_caller]
pub fn runtime(list: Box<[(ModifierSet<EcoString>, char)]>) -> Self { pub fn runtime(list: Box<[Variant<EcoString>]>) -> Self {
debug_assert!(!list.is_empty()); debug_assert!(!list.is_empty());
Self(Repr::Modified(Arc::new((List::Runtime(list), ModifierSet::default())))) Self(Repr::Modified(Arc::new((List::Runtime(list), ModifierSet::default()))))
} }
@ -93,9 +97,11 @@ impl Symbol {
match &self.0 { match &self.0 {
Repr::Single(c) => *c, Repr::Single(c) => *c,
Repr::Complex(_) => ModifierSet::<&'static str>::default() Repr::Complex(_) => ModifierSet::<&'static str>::default()
.best_match_in(self.variants()) .best_match_in(self.variants().map(|(m, c, _)| (m, c)))
.unwrap(), .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. /// 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 { if let Repr::Complex(list) = self.0 {
self.0 = self.0 =
Repr::Modified(Arc::new((List::Static(list), ModifierSet::default()))); Repr::Modified(Arc::new((List::Static(list), ModifierSet::default())));
@ -137,7 +147,12 @@ impl Symbol {
if let Repr::Modified(arc) = &mut self.0 { if let Repr::Modified(arc) = &mut self.0 {
let (list, modifiers) = Arc::make_mut(arc); let (list, modifiers) = Arc::make_mut(arc);
modifiers.insert_raw(modifier); 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); return Ok(self);
} }
} }
@ -146,7 +161,7 @@ impl Symbol {
} }
/// The characters that are covered by this 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 { match &self.0 {
Repr::Single(c) => Variants::Single(Some(*c).into_iter()), Repr::Single(c) => Variants::Single(Some(*c).into_iter()),
Repr::Complex(list) => Variants::Static(list.iter()), Repr::Complex(list) => Variants::Static(list.iter()),
@ -161,7 +176,7 @@ impl Symbol {
_ => ModifierSet::default(), _ => ModifierSet::default(),
}; };
self.variants() self.variants()
.flat_map(|(m, _)| m) .flat_map(|(m, _, _)| m)
.filter(|modifier| !modifier.is_empty() && !modifiers.contains(modifier)) .filter(|modifier| !modifier.is_empty() && !modifiers.contains(modifier))
.collect::<BTreeSet<_>>() .collect::<BTreeSet<_>>()
.into_iter() .into_iter()
@ -256,7 +271,7 @@ impl Symbol {
let list = variants let list = variants
.into_iter() .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(); .collect();
Ok(Symbol::runtime(list)) Ok(Symbol::runtime(list))
} }
@ -316,17 +331,17 @@ impl crate::foundations::Repr for Symbol {
} }
fn repr_variants<'a>( fn repr_variants<'a>(
variants: impl Iterator<Item = (ModifierSet<&'a str>, char)>, variants: impl Iterator<Item = Variant<&'a str>>,
applied_modifiers: ModifierSet<&str>, applied_modifiers: ModifierSet<&str>,
) -> String { ) -> String {
crate::foundations::repr::pretty_array_like( crate::foundations::repr::pretty_array_like(
&variants &variants
.filter(|(modifiers, _)| { .filter(|(modifiers, _, _)| {
// Only keep variants that can still be accessed, i.e., variants // Only keep variants that can still be accessed, i.e., variants
// that contain all applied modifiers. // that contain all applied modifiers.
applied_modifiers.iter().all(|am| modifiers.contains(am)) applied_modifiers.iter().all(|am| modifiers.contains(am))
}) })
.map(|(modifiers, c)| { .map(|(modifiers, c, _)| {
let trimmed_modifiers = let trimmed_modifiers =
modifiers.into_iter().filter(|&m| !applied_modifiers.contains(m)); modifiers.into_iter().filter(|&m| !applied_modifiers.contains(m));
if trimmed_modifiers.clone().all(|m| m.is_empty()) { if trimmed_modifiers.clone().all(|m| m.is_empty()) {
@ -379,18 +394,20 @@ cast! {
/// Iterator over variants. /// Iterator over variants.
enum Variants<'a> { enum Variants<'a> {
Single(std::option::IntoIter<char>), Single(std::option::IntoIter<char>),
Static(std::slice::Iter<'static, (ModifierSet<&'static str>, char)>), Static(std::slice::Iter<'static, Variant<&'static str>>),
Runtime(std::slice::Iter<'a, (ModifierSet<EcoString>, char)>), Runtime(std::slice::Iter<'a, Variant<EcoString>>),
} }
impl<'a> Iterator for Variants<'a> { impl<'a> Iterator for Variants<'a> {
type Item = (ModifierSet<&'a str>, char); type Item = Variant<&'a str>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self { 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::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. /// Try to access a field on the value.
pub fn field(&self, field: &str, sink: impl DeprecationSink) -> StrResult<Value> { pub fn field(&self, field: &str, sink: impl DeprecationSink) -> StrResult<Value> {
match self { 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::Version(version) => version.component(field).map(Self::Int),
Self::Dict(dict) => dict.get(field).cloned(), Self::Dict(dict) => dict.get(field).cloned(),
Self::Content(content) => content.field_by_name(field), 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)]| { let shorthand = |list: &[(&'static str, char)]| {
list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s) list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s)
}; };
let name = complete(variant); 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 { list.push(SymbolModel {
name, name,
@ -742,10 +736,10 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
accent: typst::math::Accent::combine(c).is_some(), accent: typst::math::Accent::combine(c).is_some(),
alternates: symbol alternates: symbol
.variants() .variants()
.filter(|(other, _)| other != &variant) .filter(|(other, _, _)| other != &variant)
.map(|(other, _)| complete(other)) .map(|(other, _, _)| complete(other))
.collect(), .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 --- --- math-attach-integral ---
// Test default of scripts attachments on integrals at display size. // 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.inter_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$
--- math-attach-large-operator --- --- math-attach-large-operator ---
// Test default of limit attachments on large operators at display size only. // Test default of limit attachments on large operators at display size only.
@ -179,7 +179,7 @@ $ a0 + a1 + a0_2 \
#{ #{
let var = $x^1$ let var = $x^1$
for i in range(24) { for i in range(24) {
var = $var$ var = $var$
} }
$var_2$ $var_2$
} }