From e6ac31e92c5bd9cc0ae26d7aa85a2e2aa59c78b7 Mon Sep 17 00:00:00 2001 From: tinger Date: Wed, 16 Jul 2025 11:53:46 +0200 Subject: [PATCH] Add version to `Binding` deprecation warnings This allows displaying a hint in which the version a binding may be removed in. It helps to signal how urgent a package update may be to package authors. The version is also sent to the proprietary doc generator. --- crates/typst-library/src/diag.rs | 15 ++++-- crates/typst-library/src/foundations/scope.rs | 32 ++++++++++--- .../typst-library/src/foundations/symbol.rs | 2 +- docs/src/lib.rs | 48 ++++++++++++++----- docs/src/model.rs | 6 ++- 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/crates/typst-library/src/diag.rs b/crates/typst-library/src/diag.rs index 2b9ff6376..1b055a8ac 100644 --- a/crates/typst-library/src/diag.rs +++ b/crates/typst-library/src/diag.rs @@ -234,18 +234,23 @@ impl From for SourceDiagnostic { /// Destination for a deprecation message when accessing a deprecated value. pub trait DeprecationSink { - /// Emits the given deprecation message into this sink. - fn emit(self, message: &str); + /// Emits the given deprecation message into this sink alongside a version + /// in which the deprecated item is planned to be removed. + fn emit(self, message: &str, until: Option<&str>); } impl DeprecationSink for () { - fn emit(self, _: &str) {} + fn emit(self, _: &str, _: Option<&str>) {} } impl DeprecationSink for (&mut Engine<'_>, Span) { /// Emits the deprecation message as a warning. - fn emit(self, message: &str) { - self.0.sink.warn(SourceDiagnostic::warning(self.1, message)); + fn emit(self, message: &str, version: Option<&str>) { + self.0.sink.warn( + SourceDiagnostic::warning(self.1, message).with_hints( + version.map(|v| eco_format!("this will be removed in {}", v)), + ), + ); } } diff --git a/crates/typst-library/src/foundations/scope.rs b/crates/typst-library/src/foundations/scope.rs index 551e3dd78..8ac7fde10 100644 --- a/crates/typst-library/src/foundations/scope.rs +++ b/crates/typst-library/src/foundations/scope.rs @@ -254,7 +254,11 @@ pub struct Binding { /// The category of the binding. category: Option, /// A deprecation message for the definition. - deprecation: Option<&'static str>, + deprecation_message: Option<&'static str>, + /// A version in which the deprecated binding is planned to be removed. + /// + /// This is ignored if `deprecation` is `None`. + deprecation_until: Option<&'static str>, } /// The different kinds of slots. @@ -274,7 +278,8 @@ impl Binding { span, kind: BindingKind::Normal, category: None, - deprecation: None, + deprecation_message: None, + deprecation_until: None, } } @@ -285,7 +290,15 @@ impl Binding { /// Marks this binding as deprecated, with the given `message`. pub fn deprecated(&mut self, message: &'static str) -> &mut Self { - self.deprecation = Some(message); + self.deprecation_message = Some(message); + self + } + + /// Set the version in which the binding is planned to be removed. + /// + /// This is ignored if [`Binding::deprecated`] isn't also set. + pub fn deprecated_until(&mut self, version: &'static str) -> &mut Self { + self.deprecation_until = Some(version); self } @@ -300,8 +313,8 @@ impl Binding { /// - pass `()` to ignore the message. /// - pass `(&mut engine, span)` to emit a warning into the engine. pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value { - if let Some(message) = self.deprecation { - sink.emit(message); + if let Some(message) = self.deprecation_message { + sink.emit(message, self.deprecation_until); } &self.value } @@ -337,8 +350,13 @@ impl Binding { } /// A deprecation message for the value, if any. - pub fn deprecation(&self) -> Option<&'static str> { - self.deprecation + pub fn deprecation_message(&self) -> Option<&'static str> { + self.deprecation_message + } + + /// The version in which a deprecated binding is planned to be removed. + pub fn deprecation_until(&self) -> Option<&'static str> { + self.deprecation_until } /// The category of the value, if any. diff --git a/crates/typst-library/src/foundations/symbol.rs b/crates/typst-library/src/foundations/symbol.rs index 898068afb..c3a13d07c 100644 --- a/crates/typst-library/src/foundations/symbol.rs +++ b/crates/typst-library/src/foundations/symbol.rs @@ -151,7 +151,7 @@ impl Symbol { modifiers.best_match_in(list.variants().map(|(m, _, d)| (m, d))) { if let Some(message) = deprecation { - sink.emit(message) + sink.emit(message, None) } return Ok(self); } diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 352b90dec..81093b1d3 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -291,8 +291,14 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel { match binding.read() { Value::Func(func) => { let name = func.name().unwrap(); - let subpage = - func_page(resolver, &route, func, path, binding.deprecation()); + let subpage = func_page( + resolver, + &route, + func, + path, + binding.deprecation_message(), + binding.deprecation_until(), + ); items.push(CategoryItem { name: name.into(), route: subpage.route.clone(), @@ -381,9 +387,11 @@ fn func_page( parent: &str, func: &Func, path: &[&str], - deprecation: Option<&'static str>, + deprecation_message: Option<&'static str>, + deprecation_until: Option<&'static str>, ) -> PageModel { - let model = func_model(resolver, func, path, false, deprecation); + let model = + func_model(resolver, func, path, false, deprecation_message, deprecation_until); let name = func.name().unwrap(); PageModel { route: eco_format!("{parent}{}/", urlify(name)), @@ -402,7 +410,8 @@ fn func_model( func: &Func, path: &[&str], nested: bool, - deprecation: Option<&'static str>, + deprecation_message: Option<&'static str>, + deprecation_until: Option<&'static str>, ) -> FuncModel { let name = func.name().unwrap(); let scope = func.scope().unwrap(); @@ -438,7 +447,8 @@ fn func_model( oneliner: oneliner(details), element: func.element().is_some(), contextual: func.contextual().unwrap_or(false), - deprecation, + deprecation_message, + deprecation_until, details: Html::markdown(resolver, details, nesting), example: example.map(|md| Html::markdown(resolver, md, None)), self_, @@ -521,7 +531,14 @@ fn scope_models(resolver: &dyn Resolver, name: &str, scope: &Scope) -> Vec() else { panic!("not a function") }; - let func = func_model(resolver, func, &path, true, binding.deprecation()); + let func = func_model( + resolver, + func, + &path, + true, + binding.deprecation_message(), + binding.deprecation_until(), + ); let id_base = urlify(&eco_format!("functions-{}", func.name)); let children = func_outline(&func, &id_base); outline_items.push(OutlineItem { @@ -669,7 +693,7 @@ fn type_model(resolver: &dyn Resolver, ty: &Type) -> TypeModel { constructor: ty .constructor() .ok() - .map(|func| func_model(resolver, &func, &[], true, None)), + .map(|func| func_model(resolver, &func, &[], true, None, None)), scope: scope_models(resolver, ty.short_name(), ty.scope()), } } @@ -718,7 +742,7 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel { } }; - for (variant, c, deprecation) in symbol.variants() { + for (variant, c, deprecation_message) in symbol.variants() { let shorthand = |list: &[(&'static str, char)]| { list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s) }; @@ -737,7 +761,9 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel { .filter(|(other, _, _)| other != &variant) .map(|(other, _, _)| complete(other)) .collect(), - deprecation: deprecation.or_else(|| binding.deprecation()), + deprecation_message: deprecation_message + .or_else(|| binding.deprecation_message()), + deprecation_until: binding.deprecation_until(), }); } } diff --git a/docs/src/model.rs b/docs/src/model.rs index e3b15e377..612592cdd 100644 --- a/docs/src/model.rs +++ b/docs/src/model.rs @@ -89,7 +89,8 @@ pub struct FuncModel { pub oneliner: EcoString, pub element: bool, pub contextual: bool, - pub deprecation: Option<&'static str>, + pub deprecation_message: Option<&'static str>, + pub deprecation_until: Option<&'static str>, pub details: Html, /// This example is only for nested function models. Others can have /// their example directly in their details. @@ -165,7 +166,8 @@ pub struct SymbolModel { pub markup_shorthand: Option<&'static str>, pub math_shorthand: Option<&'static str>, pub math_class: Option<&'static str>, - pub deprecation: Option<&'static str>, + pub deprecation_message: Option<&'static str>, + pub deprecation_until: Option<&'static str>, } /// Shorthands listed on a category page.