diff --git a/crates/typst-library/src/diag.rs b/crates/typst-library/src/diag.rs index 41b92ed65..3f2c5a6cc 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 838584ccd..efed73bc1 100644 --- a/crates/typst-library/src/foundations/scope.rs +++ b/crates/typst-library/src/foundations/scope.rs @@ -255,6 +255,10 @@ pub struct Binding { category: Option, /// A deprecation message for the definition. deprecation: Option<&'static str>, + /// A version in which the deprecated binding is planned to be removed. + /// + /// This is ignored if `deprecation` is `None`. + until: Option<&'static str>, } /// The different kinds of slots. @@ -275,6 +279,7 @@ impl Binding { kind: BindingKind::Normal, category: None, deprecation: None, + until: None, } } @@ -289,6 +294,14 @@ impl Binding { 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 until(&mut self, version: &'static str) -> &mut Self { + self.until = Some(version); + self + } + /// Read the value. pub fn read(&self) -> &Value { &self.value @@ -301,7 +314,7 @@ impl Binding { /// - 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); + sink.emit(message, self.until); } &self.value } diff --git a/crates/typst-library/src/foundations/symbol.rs b/crates/typst-library/src/foundations/symbol.rs index f57bb0c2a..953135c2d 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/crates/typst-library/src/loading/cbor.rs b/crates/typst-library/src/loading/cbor.rs index d95f73844..c17d679fe 100644 --- a/crates/typst-library/src/loading/cbor.rs +++ b/crates/typst-library/src/loading/cbor.rs @@ -33,7 +33,10 @@ pub fn cbor( impl cbor { /// Reads structured data from CBOR bytes. #[func(title = "Decode CBOR")] - #[deprecated = "`cbor.decode` is deprecated, directly pass bytes to `cbor` instead"] + #[deprecated( + message = "`cbor.decode` is deprecated, directly pass bytes to `cbor` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// CBOR data. diff --git a/crates/typst-library/src/loading/csv.rs b/crates/typst-library/src/loading/csv.rs index d5b54a06c..ebcff4a09 100644 --- a/crates/typst-library/src/loading/csv.rs +++ b/crates/typst-library/src/loading/csv.rs @@ -95,7 +95,10 @@ pub fn csv( impl csv { /// Reads structured data from a CSV string/bytes. #[func(title = "Decode CSV")] - #[deprecated = "`csv.decode` is deprecated, directly pass bytes to `csv` instead"] + #[deprecated( + message = "`csv.decode` is deprecated, directly pass bytes to `csv` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// CSV data. diff --git a/crates/typst-library/src/loading/json.rs b/crates/typst-library/src/loading/json.rs index 7d0732ba0..b424cf2ec 100644 --- a/crates/typst-library/src/loading/json.rs +++ b/crates/typst-library/src/loading/json.rs @@ -67,7 +67,10 @@ pub fn json( impl json { /// Reads structured data from a JSON string/bytes. #[func(title = "Decode JSON")] - #[deprecated = "`json.decode` is deprecated, directly pass bytes to `json` instead"] + #[deprecated( + message = "`json.decode` is deprecated, directly pass bytes to `json` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// JSON data. diff --git a/crates/typst-library/src/loading/toml.rs b/crates/typst-library/src/loading/toml.rs index a4252feca..cfca10f74 100644 --- a/crates/typst-library/src/loading/toml.rs +++ b/crates/typst-library/src/loading/toml.rs @@ -41,7 +41,10 @@ pub fn toml( impl toml { /// Reads structured data from a TOML string/bytes. #[func(title = "Decode TOML")] - #[deprecated = "`toml.decode` is deprecated, directly pass bytes to `toml` instead"] + #[deprecated( + message = "`toml.decode` is deprecated, directly pass bytes to `toml` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// TOML data. diff --git a/crates/typst-library/src/loading/xml.rs b/crates/typst-library/src/loading/xml.rs index 0023c5df5..a003911ea 100644 --- a/crates/typst-library/src/loading/xml.rs +++ b/crates/typst-library/src/loading/xml.rs @@ -75,7 +75,10 @@ pub fn xml( impl xml { /// Reads structured data from an XML string/bytes. #[func(title = "Decode XML")] - #[deprecated = "`xml.decode` is deprecated, directly pass bytes to `xml` instead"] + #[deprecated( + message = "`xml.decode` is deprecated, directly pass bytes to `xml` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// XML data. diff --git a/crates/typst-library/src/loading/yaml.rs b/crates/typst-library/src/loading/yaml.rs index 0edf1f901..2330a344b 100644 --- a/crates/typst-library/src/loading/yaml.rs +++ b/crates/typst-library/src/loading/yaml.rs @@ -54,7 +54,10 @@ pub fn yaml( impl yaml { /// Reads structured data from a YAML string/bytes. #[func(title = "Decode YAML")] - #[deprecated = "`yaml.decode` is deprecated, directly pass bytes to `yaml` instead"] + #[deprecated( + message = "`yaml.decode` is deprecated, directly pass bytes to `yaml` instead", + until = "0.15.0" + )] pub fn decode( engine: &mut Engine, /// YAML data. diff --git a/crates/typst-library/src/visualize/image/mod.rs b/crates/typst-library/src/visualize/image/mod.rs index f1fa6381b..f599684d2 100644 --- a/crates/typst-library/src/visualize/image/mod.rs +++ b/crates/typst-library/src/visualize/image/mod.rs @@ -169,7 +169,10 @@ pub struct ImageElem { impl ImageElem { /// Decode a raster or vector graphic from bytes or a string. #[func(title = "Decode Image")] - #[deprecated = "`image.decode` is deprecated, directly pass bytes to `image` instead"] + #[deprecated( + message = "`image.decode` is deprecated, directly pass bytes to `image` instead", + until = "0.15.0" + )] pub fn decode( span: Span, /// The data to decode as an image. Can be a string for SVGs. diff --git a/crates/typst-library/src/visualize/mod.rs b/crates/typst-library/src/visualize/mod.rs index 72a420657..ce712c145 100644 --- a/crates/typst-library/src/visualize/mod.rs +++ b/crates/typst-library/src/visualize/mod.rs @@ -46,6 +46,7 @@ pub(super) fn define(global: &mut Scope) { .deprecated("the `path` function is deprecated, use `curve` instead"); global .define("pattern", Type::of::()) - .deprecated("the name `pattern` is deprecated, use `tiling` instead"); + .deprecated("the name `pattern` is deprecated, use `tiling` instead") + .until("0.15.0"); global.reset_category(); } diff --git a/crates/typst-macros/src/scope.rs b/crates/typst-macros/src/scope.rs index 392ab1a53..d201447c7 100644 --- a/crates/typst-macros/src/scope.rs +++ b/crates/typst-macros/src/scope.rs @@ -1,7 +1,8 @@ use heck::ToKebabCase; use proc_macro2::TokenStream; use quote::quote; -use syn::{parse_quote, Result}; +use syn::punctuated::Punctuated; +use syn::{parse_quote, MetaNameValue, Result, Token}; use crate::util::{foundations, BareType}; @@ -53,13 +54,31 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result { _ => bail!(child, "unexpected item in scope"), }; - if let Some(message) = attrs.iter().find_map(|attr| match &attr.meta { - syn::Meta::NameValue(pair) if pair.path.is_ident("deprecated") => { - Some(&pair.value) + if let Some(attr) = attrs.iter().find(|attr| attr.path().is_ident("deprecated")) { + match &attr.meta { + syn::Meta::NameValue(pair) if pair.path.is_ident("deprecated") => { + let message = &pair.value; + def = quote! { #def.deprecated(#message) } + } + syn::Meta::List(list) if list.path.is_ident("deprecated") => { + let args = list.parse_args_with( + Punctuated::::parse_separated_nonempty, + )?; + + if let Some(message) = args.iter().find_map(|pair| { + pair.path.is_ident("message").then_some(&pair.value) + }) { + def = quote! { #def.deprecated(#message) } + } + + if let Some(version) = args.iter().find_map(|pair| { + pair.path.is_ident("until").then_some(&pair.value) + }) { + def = quote! { #def.until(#version) } + } + } + _ => {} } - _ => None, - }) { - def = quote! { #def.deprecated(#message) } } definitions.push(def); diff --git a/tests/suite/loading/cbor.typ b/tests/suite/loading/cbor.typ index 4b50bb9c3..526ef8c53 100644 --- a/tests/suite/loading/cbor.typ +++ b/tests/suite/loading/cbor.typ @@ -1,3 +1,4 @@ --- cbor-decode-deprecated --- // Warning: 15-21 `cbor.decode` is deprecated, directly pass bytes to `cbor` instead +// Hint: 15-21 this will be removed in 0.15.0 #let _ = cbor.decode diff --git a/tests/suite/loading/csv.typ b/tests/suite/loading/csv.typ index 046345bec..ffcaa8508 100644 --- a/tests/suite/loading/csv.typ +++ b/tests/suite/loading/csv.typ @@ -32,4 +32,5 @@ --- csv-decode-deprecated --- // Warning: 14-20 `csv.decode` is deprecated, directly pass bytes to `csv` instead +// Hint: 14-20 this will be removed in 0.15.0 #let _ = csv.decode diff --git a/tests/suite/loading/json.typ b/tests/suite/loading/json.typ index 9e433992d..5675033af 100644 --- a/tests/suite/loading/json.typ +++ b/tests/suite/loading/json.typ @@ -11,6 +11,7 @@ --- json-decode-deprecated --- // Warning: 15-21 `json.decode` is deprecated, directly pass bytes to `json` instead +// Hint: 15-21 this will be removed in 0.15.0 #let _ = json.decode --- issue-3363-json-large-number --- diff --git a/tests/suite/loading/toml.typ b/tests/suite/loading/toml.typ index 9d65da452..e5df31118 100644 --- a/tests/suite/loading/toml.typ +++ b/tests/suite/loading/toml.typ @@ -42,4 +42,5 @@ --- toml-decode-deprecated --- // Warning: 15-21 `toml.decode` is deprecated, directly pass bytes to `toml` instead +// Hint: 15-21 this will be removed in 0.15.0 #let _ = toml.decode diff --git a/tests/suite/loading/xml.typ b/tests/suite/loading/xml.typ index eed7db0ae..b29388410 100644 --- a/tests/suite/loading/xml.typ +++ b/tests/suite/loading/xml.typ @@ -29,4 +29,5 @@ --- xml-decode-deprecated --- // Warning: 14-20 `xml.decode` is deprecated, directly pass bytes to `xml` instead +// Hint: 14-20 this will be removed in 0.15.0 #let _ = xml.decode diff --git a/tests/suite/loading/yaml.typ b/tests/suite/loading/yaml.typ index ad171c6ef..ac47f182e 100644 --- a/tests/suite/loading/yaml.typ +++ b/tests/suite/loading/yaml.typ @@ -18,4 +18,5 @@ --- yaml-decode-deprecated --- // Warning: 15-21 `yaml.decode` is deprecated, directly pass bytes to `yaml` instead +// Hint: 15-21 this will be removed in 0.15.0 #let _ = yaml.decode diff --git a/tests/suite/visualize/image.typ b/tests/suite/visualize/image.typ index 36ec06cb1..3f4701e55 100644 --- a/tests/suite/visualize/image.typ +++ b/tests/suite/visualize/image.typ @@ -188,26 +188,31 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B --- image-decode-svg --- // Test parsing from svg data // Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead +// Hint: 8-14 this will be removed in 0.15.0 #image.decode(``.text, format: "svg") --- image-decode-bad-svg --- // Error: 15-152 failed to parse SVG (missing root node at 1:1) // Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead +// Hint: 8-14 this will be removed in 0.15.0 #image.decode(``.text, format: "svg") --- image-decode-detect-format --- // Test format auto detect // Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead +// Hint: 8-14 this will be removed in 0.15.0 #image.decode(read("/assets/images/tiger.jpg", encoding: none), width: 80%) --- image-decode-specify-format --- // Test format manual // Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead +// Hint: 8-14 this will be removed in 0.15.0 #image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "jpg", width: 80%) --- image-decode-specify-wrong-format --- // Error: 2-91 failed to decode image (Format error decoding Png: Invalid PNG signature.) // Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead +// Hint: 8-14 this will be removed in 0.15.0 #image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "png", width: 80%) --- image-pixmap-empty --- diff --git a/tests/suite/visualize/tiling.typ b/tests/suite/visualize/tiling.typ index 904133411..a16f4fe92 100644 --- a/tests/suite/visualize/tiling.typ +++ b/tests/suite/visualize/tiling.typ @@ -161,5 +161,6 @@ #set page(width: auto, height: auto, margin: 0pt) // Warning: 10-17 the name `pattern` is deprecated, use `tiling` instead +// Hint: 10-17 this will be removed in 0.15.0 #let t = pattern(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%))) #rect(width: 50pt, height: 50pt, fill: t)