mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
Added hint
to bail!
, warning!
, and error!
(#2756)
This commit is contained in:
parent
219c1c9ed0
commit
2c85161a27
@ -18,69 +18,105 @@ use crate::{World, WorldExt};
|
|||||||
/// `StrResult`. If called with a span, a string and format args, returns
|
/// `StrResult`. If called with a span, a string and format args, returns
|
||||||
/// a `SourceResult`.
|
/// a `SourceResult`.
|
||||||
///
|
///
|
||||||
|
/// You can also emit hints with the `; hint: "..."` syntax.
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// bail!("bailing with a {}", "string result");
|
/// bail!("bailing with a {}", "string result");
|
||||||
/// bail!(span, "bailing with a {}", "source result");
|
/// bail!(span, "bailing with a {}", "source result");
|
||||||
|
/// bail!(
|
||||||
|
/// span, "bailing with a {}", "source result";
|
||||||
|
/// hint: "hint 1"
|
||||||
|
/// );
|
||||||
|
/// bail!(
|
||||||
|
/// span, "bailing with a {}", "source result";
|
||||||
|
/// hint: "hint 1";
|
||||||
|
/// hint: "hint 2";
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! __bail {
|
macro_rules! __bail {
|
||||||
|
// For bail!("just a {}", "string")
|
||||||
($fmt:literal $(, $arg:expr)* $(,)?) => {
|
($fmt:literal $(, $arg:expr)* $(,)?) => {
|
||||||
return Err($crate::diag::eco_format!($fmt, $($arg),*))
|
return Err($crate::diag::error!(
|
||||||
|
$fmt, $($arg),*
|
||||||
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For bail!(error!(..))
|
||||||
($error:expr) => {
|
($error:expr) => {
|
||||||
return Err(::ecow::eco_vec![$error])
|
return Err(::ecow::eco_vec![$error])
|
||||||
};
|
};
|
||||||
|
|
||||||
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
|
// For bail(span, ...)
|
||||||
return Err(::ecow::eco_vec![$crate::diag::SourceDiagnostic::error(
|
($($tts:tt)*) => {
|
||||||
$span,
|
return Err(::ecow::eco_vec![$crate::diag::error!($($tts)*)])
|
||||||
$crate::diag::eco_format!($fmt, $($arg),*),
|
|
||||||
)])
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use crate::__bail as bail;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use crate::__error as error;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use crate::__warning as warning;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use ecow::eco_format;
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use ecow::EcoString;
|
|
||||||
|
|
||||||
/// Construct an [`EcoString`] or [`SourceDiagnostic`] with severity `Error`.
|
/// Construct an [`EcoString`] or [`SourceDiagnostic`] with severity `Error`.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! __error {
|
macro_rules! __error {
|
||||||
|
// For bail!("just a {}", "string").
|
||||||
($fmt:literal $(, $arg:expr)* $(,)?) => {
|
($fmt:literal $(, $arg:expr)* $(,)?) => {
|
||||||
$crate::diag::eco_format!($fmt, $($arg),*)
|
$crate::diag::eco_format!($fmt, $($arg),*)
|
||||||
};
|
};
|
||||||
|
|
||||||
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
|
// For bail!(span, ...)
|
||||||
|
(
|
||||||
|
$span:expr, $fmt:literal $(, $arg:expr)*
|
||||||
|
$(; hint: $hint:literal $(, $hint_arg:expr)*)*
|
||||||
|
$(,)?
|
||||||
|
) => {
|
||||||
$crate::diag::SourceDiagnostic::error(
|
$crate::diag::SourceDiagnostic::error(
|
||||||
$span,
|
$span,
|
||||||
$crate::diag::eco_format!($fmt, $($arg),*),
|
$crate::diag::eco_format!($fmt, $($arg),*),
|
||||||
)
|
) $(.with_hint($crate::diag::eco_format!($hint, $($hint_arg),*)))*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a [`SourceDiagnostic`] with severity `Warning`.
|
/// Construct a [`SourceDiagnostic`] with severity `Warning`.
|
||||||
|
///
|
||||||
|
/// You can also emit hints with the `; hint: "..."` syntax.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// warning!(span, "warning with a {}", "source result");
|
||||||
|
/// warning!(
|
||||||
|
/// span, "warning with a {}", "source result";
|
||||||
|
/// hint: "hint 1"
|
||||||
|
/// );
|
||||||
|
/// warning!(
|
||||||
|
/// span, "warning with a {}", "source result";
|
||||||
|
/// hint: "hint 1";
|
||||||
|
/// hint: "hint 2";
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! __warning {
|
macro_rules! __warning {
|
||||||
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
|
(
|
||||||
|
$span:expr,
|
||||||
|
$fmt:literal $(, $arg:expr)*
|
||||||
|
$(; hint: $hint:literal $(, $hint_arg:expr)*)*
|
||||||
|
$(,)?
|
||||||
|
) => {
|
||||||
$crate::diag::SourceDiagnostic::warning(
|
$crate::diag::SourceDiagnostic::warning(
|
||||||
$span,
|
$span,
|
||||||
$crate::diag::eco_format!($fmt, $($arg),*),
|
$crate::diag::eco_format!($fmt, $($arg),*),
|
||||||
)
|
) $(.with_hint($crate::diag::eco_format!($hint, $($hint_arg),*)))*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use {
|
||||||
|
crate::__bail as bail,
|
||||||
|
crate::__error as error,
|
||||||
|
crate::__warning as warning,
|
||||||
|
ecow::{eco_format, EcoString},
|
||||||
|
};
|
||||||
|
|
||||||
/// A result that can carry multiple source errors.
|
/// A result that can carry multiple source errors.
|
||||||
pub type SourceResult<T> = Result<T, EcoVec<SourceDiagnostic>>;
|
pub type SourceResult<T> = Result<T, EcoVec<SourceDiagnostic>>;
|
||||||
|
|
||||||
|
@ -139,11 +139,12 @@ impl Eval for ast::Strong<'_> {
|
|||||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let body = self.body();
|
let body = self.body();
|
||||||
if body.exprs().next().is_none() {
|
if body.exprs().next().is_none() {
|
||||||
vm.engine.tracer.warn(
|
vm.engine
|
||||||
warning!(self.span(), "no text within stars").with_hint(
|
.tracer
|
||||||
"using multiple consecutive stars (e.g. **) has no additional effect",
|
.warn(warning!(
|
||||||
),
|
self.span(), "no text within stars";
|
||||||
);
|
hint: "using multiple consecutive stars (e.g. **) has no additional effect",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(StrongElem::new(body.eval(vm)?).pack())
|
Ok(StrongElem::new(body.eval(vm)?).pack())
|
||||||
@ -159,9 +160,10 @@ impl Eval for ast::Emph<'_> {
|
|||||||
if body.exprs().next().is_none() {
|
if body.exprs().next().is_none() {
|
||||||
vm.engine
|
vm.engine
|
||||||
.tracer
|
.tracer
|
||||||
.warn(warning!(self.span(), "no text within underscores").with_hint(
|
.warn(warning!(
|
||||||
"using multiple consecutive underscores (e.g. __) has no additional effect"
|
self.span(), "no text within underscores";
|
||||||
));
|
hint: "using multiple consecutive underscores (e.g. __) has no additional effect"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EmphElem::new(body.eval(vm)?).pack())
|
Ok(EmphElem::new(body.eval(vm)?).pack())
|
||||||
|
@ -5,7 +5,7 @@ use std::iter::repeat;
|
|||||||
|
|
||||||
use ecow::{eco_format, EcoString, EcoVec};
|
use ecow::{eco_format, EcoString, EcoVec};
|
||||||
|
|
||||||
use crate::diag::{bail, error, StrResult};
|
use crate::diag::{bail, StrResult};
|
||||||
use crate::foundations::{cast, func, repr, scope, ty, Repr};
|
use crate::foundations::{cast, func, repr, scope, ty, Repr};
|
||||||
|
|
||||||
/// A version with an arbitrary number of components.
|
/// A version with an arbitrary number of components.
|
||||||
@ -43,7 +43,7 @@ impl Version {
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(Self::COMPONENTS)
|
.zip(Self::COMPONENTS)
|
||||||
.find_map(|(&i, s)| (s == name).then_some(i as i64))
|
.find_map(|(&i, s)| (s == name).then_some(i as i64))
|
||||||
.ok_or_else(|| error!("unknown version component"))
|
.ok_or_else(|| "unknown version component".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a component to the end of this version.
|
/// Push a component to the end of this version.
|
||||||
|
@ -71,7 +71,7 @@ pub(crate) use self::inline::*;
|
|||||||
|
|
||||||
use comemo::{Tracked, TrackedMut};
|
use comemo::{Tracked, TrackedMut};
|
||||||
|
|
||||||
use crate::diag::{bail, error, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
||||||
@ -238,8 +238,10 @@ impl Layout for Content {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if engine.route.exceeding() {
|
if engine.route.exceeding() {
|
||||||
bail!(error!(content.span(), "maximum layout depth exceeded")
|
bail!(
|
||||||
.with_hint("try to reduce the amount of nesting in your layout"));
|
content.span(), "maximum layout depth exceeded";
|
||||||
|
hint: "try to reduce the amount of nesting in your layout",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
|
@ -143,10 +143,10 @@ fn typeset(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if iter >= 5 {
|
if iter >= 5 {
|
||||||
tracer.warn(
|
tracer.warn(warning!(
|
||||||
warning!(Span::detached(), "layout did not converge within 5 attempts",)
|
Span::detached(), "layout did not converge within 5 attempts";
|
||||||
.with_hint("check if any states or queries are updating themselves"),
|
hint: "check if any states or queries are updating themselves"
|
||||||
);
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
|
|
||||||
use crate::diag::{bail, error, At, SourceResult, StrResult};
|
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, scope, Content, Finalize, Label, NativeElement, Show, Smart, StyleChain,
|
cast, elem, scope, Content, Finalize, Label, NativeElement, Show, Smart, StyleChain,
|
||||||
@ -280,9 +280,10 @@ impl Show for FootnoteEntry {
|
|||||||
let numbering = note.numbering(default);
|
let numbering = note.numbering(default);
|
||||||
let counter = Counter::of(FootnoteElem::elem());
|
let counter = Counter::of(FootnoteElem::elem());
|
||||||
let Some(loc) = note.location() else {
|
let Some(loc) = note.location() else {
|
||||||
bail!(error!(self.span(), "footnote entry must have a location").with_hint(
|
bail!(
|
||||||
"try using a query or a show rule to customize the footnote instead"
|
self.span(), "footnote entry must have a location";
|
||||||
))
|
hint: "try using a query or a show rule to customize the footnote instead"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let num = counter.at(engine, loc)?.display(engine, numbering)?;
|
let num = counter.at(engine, loc)?.display(engine, numbering)?;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::diag::{bail, error, At, SourceResult};
|
use crate::diag::{bail, At, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, scope, select_where, Content, Finalize, Func, LocatableSelector,
|
cast, elem, scope, select_where, Content, Finalize, Func, LocatableSelector,
|
||||||
@ -492,10 +492,10 @@ impl Show for OutlineEntry {
|
|||||||
// In case a user constructs an outline entry with an arbitrary element.
|
// In case a user constructs an outline entry with an arbitrary element.
|
||||||
let Some(location) = elem.location() else {
|
let Some(location) = elem.location() else {
|
||||||
if elem.can::<dyn Locatable>() && elem.can::<dyn Outlinable>() {
|
if elem.can::<dyn Locatable>() && elem.can::<dyn Outlinable>() {
|
||||||
bail!(error!(self.span(), "{} must have a location", elem.func().name())
|
bail!(
|
||||||
.with_hint(
|
self.span(), "{} must have a location", elem.func().name();
|
||||||
"try using a query or a show rule to customize the outline.entry instead",
|
hint: "try using a query or a show rule to customize the outline.entry instead",
|
||||||
))
|
)
|
||||||
} else {
|
} else {
|
||||||
bail!(self.span(), "cannot outline {}", elem.func().name())
|
bail!(self.span(), "cannot outline {}", elem.func().name())
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use std::mem;
|
|||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
|
|
||||||
use crate::diag::{bail, error, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Content, Finalize, Guard, NativeElement, Recipe, Selector, Show, StyleChain,
|
Content, Finalize, Guard, NativeElement, Recipe, Selector, Show, StyleChain,
|
||||||
@ -308,11 +308,11 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
if let Some(realized) = realize(self.engine, content, styles)? {
|
if let Some(realized) = realize(self.engine, content, styles)? {
|
||||||
self.engine.route.increase();
|
self.engine.route.increase();
|
||||||
if self.engine.route.exceeding() {
|
if self.engine.route.exceeding() {
|
||||||
bail!(error!(content.span(), "maximum show rule depth exceeded")
|
bail!(
|
||||||
.with_hint("check whether the show rule matches its own output")
|
content.span(), "maximum show rule depth exceeded";
|
||||||
.with_hint(
|
hint: "check whether the show rule matches its own output";
|
||||||
"this is a current compiler limitation that will be resolved in the future"
|
hint: "this is a current compiler limitation that will be resolved in the future",
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
let stored = self.scratch.content.alloc(realized);
|
let stored = self.scratch.content.alloc(realized);
|
||||||
let v = self.accept(stored, styles);
|
let v = self.accept(stored, styles);
|
||||||
|
@ -34,7 +34,7 @@ use ecow::{eco_format, EcoString};
|
|||||||
use rustybuzz::{Feature, Tag};
|
use rustybuzz::{Feature, Tag};
|
||||||
use ttf_parser::Rect;
|
use ttf_parser::Rect;
|
||||||
|
|
||||||
use crate::diag::{bail, error, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
|
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
|
||||||
@ -228,11 +228,9 @@ pub struct TextElem {
|
|||||||
if let Some(paint) = &paint {
|
if let Some(paint) = &paint {
|
||||||
if paint.v.relative() == Smart::Custom(RelativeTo::Self_) {
|
if paint.v.relative() == Smart::Custom(RelativeTo::Self_) {
|
||||||
bail!(
|
bail!(
|
||||||
error!(
|
paint.span,
|
||||||
paint.span,
|
"gradients and patterns on text must be relative to the parent";
|
||||||
"gradients and patterns on text must be relative to the parent"
|
hint: "make sure to set `relative: auto` on your text fill"
|
||||||
)
|
|
||||||
.with_hint("make sure to set `relative: auto` on your text fill")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use palette::{
|
|||||||
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
|
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::diag::{bail, error, At, SourceResult, StrResult};
|
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
array, cast, func, repr, scope, ty, Args, Array, IntoValue, Module, Repr, Scope, Str,
|
array, cast, func, repr, scope, ty, Args, Array, IntoValue, Module, Repr, Scope, Str,
|
||||||
Value,
|
Value,
|
||||||
@ -922,8 +922,10 @@ impl Color {
|
|||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Luma(_) => {
|
Self::Luma(_) => {
|
||||||
bail!(error!(span, "cannot saturate grayscale color")
|
bail!(
|
||||||
.with_hint("try converting your color to RGB first"));
|
span, "cannot saturate grayscale color";
|
||||||
|
hint: "try converting your color to RGB first"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Self::Oklab(_) => self.to_hsv().saturate(span, factor)?.to_oklab(),
|
Self::Oklab(_) => self.to_hsv().saturate(span, factor)?.to_oklab(),
|
||||||
Self::Oklch(_) => self.to_hsv().saturate(span, factor)?.to_oklch(),
|
Self::Oklch(_) => self.to_hsv().saturate(span, factor)?.to_oklch(),
|
||||||
@ -946,8 +948,10 @@ impl Color {
|
|||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Luma(_) => {
|
Self::Luma(_) => {
|
||||||
bail!(error!(span, "cannot desaturate grayscale color")
|
bail!(
|
||||||
.with_hint("try converting your color to RGB first"));
|
span, "cannot desaturate grayscale color";
|
||||||
|
hint: "try converting your color to RGB first"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Self::Oklab(_) => self.to_hsv().desaturate(span, factor)?.to_oklab(),
|
Self::Oklab(_) => self.to_hsv().desaturate(span, factor)?.to_oklab(),
|
||||||
Self::Oklch(_) => self.to_hsv().desaturate(span, factor)?.to_oklch(),
|
Self::Oklch(_) => self.to_hsv().desaturate(span, factor)?.to_oklch(),
|
||||||
|
@ -6,7 +6,7 @@ use std::sync::Arc;
|
|||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use kurbo::Vec2;
|
use kurbo::Vec2;
|
||||||
|
|
||||||
use crate::diag::{bail, error, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
array, cast, func, scope, ty, Args, Array, Cast, Func, IntoValue, Repr, Smart,
|
array, cast, func, scope, ty, Args, Array, Cast, Func, IntoValue, Repr, Smart,
|
||||||
};
|
};
|
||||||
@ -231,8 +231,10 @@ impl Gradient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if stops.len() < 2 {
|
if stops.len() < 2 {
|
||||||
bail!(error!(span, "a gradient must have at least two stops")
|
bail!(
|
||||||
.with_hint("try filling the shape with a single color instead"));
|
span, "a gradient must have at least two stops";
|
||||||
|
hint: "try filling the shape with a single color instead"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self::Linear(Arc::new(LinearGradient {
|
Ok(Self::Linear(Arc::new(LinearGradient {
|
||||||
@ -332,24 +334,29 @@ impl Gradient {
|
|||||||
focal_radius: Spanned<Ratio>,
|
focal_radius: Spanned<Ratio>,
|
||||||
) -> SourceResult<Gradient> {
|
) -> SourceResult<Gradient> {
|
||||||
if stops.len() < 2 {
|
if stops.len() < 2 {
|
||||||
bail!(error!(span, "a gradient must have at least two stops")
|
bail!(
|
||||||
.with_hint("try filling the shape with a single color instead"));
|
span, "a gradient must have at least two stops";
|
||||||
|
hint: "try filling the shape with a single color instead"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if focal_radius.v > radius.v {
|
if focal_radius.v > radius.v {
|
||||||
bail!(error!(
|
bail!(
|
||||||
focal_radius.span,
|
focal_radius.span,
|
||||||
"the focal radius must be smaller than the end radius"
|
"the focal radius must be smaller than the end radius";
|
||||||
)
|
hint: "try using a focal radius of `0%` instead"
|
||||||
.with_hint("try using a focal radius of `0%` instead"));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let focal_center = focal_center.unwrap_or(center);
|
let focal_center = focal_center.unwrap_or(center);
|
||||||
let d_center_sqr = (focal_center.x - center.x).get().powi(2)
|
let d_center_sqr = (focal_center.x - center.x).get().powi(2)
|
||||||
+ (focal_center.y - center.y).get().powi(2);
|
+ (focal_center.y - center.y).get().powi(2);
|
||||||
if d_center_sqr.sqrt() >= (radius.v - focal_radius.v).get() {
|
if d_center_sqr.sqrt() >= (radius.v - focal_radius.v).get() {
|
||||||
bail!(error!(span, "the focal circle must be inside of the end circle")
|
bail!(
|
||||||
.with_hint("try using a focal center of `auto` instead"));
|
span,
|
||||||
|
"the focal circle must be inside of the end circle";
|
||||||
|
hint: "try using a focal center of `auto` instead"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Gradient::Radial(Arc::new(RadialGradient {
|
Ok(Gradient::Radial(Arc::new(RadialGradient {
|
||||||
@ -419,8 +426,10 @@ impl Gradient {
|
|||||||
center: Axes<Ratio>,
|
center: Axes<Ratio>,
|
||||||
) -> SourceResult<Gradient> {
|
) -> SourceResult<Gradient> {
|
||||||
if stops.len() < 2 {
|
if stops.len() < 2 {
|
||||||
bail!(error!(span, "a gradient must have at least two stops")
|
bail!(
|
||||||
.with_hint("try filling the shape with a single color instead"));
|
span, "a gradient must have at least two stops";
|
||||||
|
hint: "try filling the shape with a single color instead"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Gradient::Conic(Arc::new(ConicGradient {
|
Ok(Gradient::Conic(Arc::new(ConicGradient {
|
||||||
@ -1154,11 +1163,10 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
|
|||||||
let mut last_stop = f64::NEG_INFINITY;
|
let mut last_stop = f64::NEG_INFINITY;
|
||||||
for Spanned { v: stop, span } in stops.iter() {
|
for Spanned { v: stop, span } in stops.iter() {
|
||||||
let Some(stop) = stop.offset else {
|
let Some(stop) = stop.offset else {
|
||||||
bail!(error!(
|
bail!(
|
||||||
*span,
|
*span, "either all stops must have an offset or none of them can";
|
||||||
"either all stops must have an offset or none of them can"
|
hint: "try adding an offset to all stops"
|
||||||
)
|
);
|
||||||
.with_hint("try adding an offset to all stops"));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if stop.get() < last_stop {
|
if stop.get() < last_stop {
|
||||||
@ -1179,13 +1187,19 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
|
|||||||
.collect::<SourceResult<Vec<_>>>()?;
|
.collect::<SourceResult<Vec<_>>>()?;
|
||||||
|
|
||||||
if out[0].1 != Ratio::zero() {
|
if out[0].1 != Ratio::zero() {
|
||||||
bail!(error!(stops[0].span, "first stop must have an offset of 0%")
|
bail!(
|
||||||
.with_hint("try setting this stop to `0%`"));
|
stops[0].span,
|
||||||
|
"first stop must have an offset of 0";
|
||||||
|
hint: "try setting this stop to `0%`"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if out[out.len() - 1].1 != Ratio::one() {
|
if out[out.len() - 1].1 != Ratio::one() {
|
||||||
bail!(error!(stops[0].span, "last stop must have an offset of 100%")
|
bail!(
|
||||||
.with_hint("try setting this stop to `100%`"));
|
stops[out.len() - 1].span,
|
||||||
|
"last stop must have an offset of 100%";
|
||||||
|
hint: "try setting this stop to `100%`"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(out);
|
return Ok(out);
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
|
|
||||||
use crate::diag::{bail, error, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, scope, ty, Content, Repr, Smart, StyleChain};
|
use crate::foundations::{func, scope, ty, Content, Repr, Smart, StyleChain};
|
||||||
use crate::layout::{Abs, Axes, Em, Frame, Layout, Length, Regions, Size};
|
use crate::layout::{Abs, Axes, Em, Frame, Layout, Length, Regions, Size};
|
||||||
@ -181,8 +181,10 @@ impl Pattern {
|
|||||||
|
|
||||||
// Check that the frame is non-zero.
|
// Check that the frame is non-zero.
|
||||||
if size.is_auto() && frame.size().is_zero() {
|
if size.is_auto() && frame.size().is_zero() {
|
||||||
bail!(error!(span, "pattern tile size must be non-zero")
|
bail!(
|
||||||
.with_hint("try setting the size manually"));
|
span, "pattern tile size must be non-zero";
|
||||||
|
hint: "try setting the size manually"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the size of the frame if the size is enforced.
|
// Set the size of the frame if the size is enforced.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user