summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst-cli/src/main.rs30
-rw-r--r--crates/typst-cli/src/query.rs6
-rw-r--r--crates/typst-macros/src/cast.rs2
-rw-r--r--crates/typst/src/diag.rs83
-rw-r--r--crates/typst/src/eval/code.rs12
-rw-r--r--crates/typst/src/eval/ops.rs36
-rw-r--r--crates/typst/src/foundations/args.rs4
-rw-r--r--crates/typst/src/foundations/array.rs10
-rw-r--r--crates/typst/src/foundations/auto.rs6
-rw-r--r--crates/typst/src/foundations/cast.rs18
-rw-r--r--crates/typst/src/foundations/none.rs6
-rw-r--r--crates/typst/src/foundations/scope.rs9
-rw-r--r--crates/typst/src/foundations/selector.rs6
-rw-r--r--crates/typst/src/foundations/value.rs12
-rw-r--r--crates/typst/src/introspection/counter.rs4
-rw-r--r--crates/typst/src/layout/align.rs4
-rw-r--r--crates/typst/src/layout/corners.rs6
-rw-r--r--crates/typst/src/layout/grid/cells.rs8
-rw-r--r--crates/typst/src/layout/grid/mod.rs39
-rw-r--r--crates/typst/src/layout/sides.rs6
-rw-r--r--crates/typst/src/math/matrix.rs4
-rw-r--r--crates/typst/src/model/bibliography.rs6
-rw-r--r--crates/typst/src/model/document.rs6
-rw-r--r--crates/typst/src/model/table.rs33
-rw-r--r--crates/typst/src/text/lang.rs20
-rw-r--r--crates/typst/src/text/mod.rs34
-rw-r--r--crates/typst/src/text/raw.rs4
-rw-r--r--crates/typst/src/text/smartquote.rs4
-rw-r--r--crates/typst/src/visualize/stroke.rs4
-rw-r--r--tests/suite/layout/grid/cell.typ3
-rw-r--r--tests/suite/layout/grid/footers.typ6
-rw-r--r--tests/suite/layout/grid/headers.typ6
-rw-r--r--tests/suite/layout/grid/stroke.typ12
-rw-r--r--tests/suite/layout/table.typ3
34 files changed, 267 insertions, 185 deletions
diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs
index c8bd3914..bc1c30a6 100644
--- a/crates/typst-cli/src/main.rs
+++ b/crates/typst-cli/src/main.rs
@@ -20,6 +20,7 @@ use clap::Parser;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::WriteColor;
use once_cell::sync::Lazy;
+use typst::diag::HintedStrResult;
use crate::args::{CliArguments, Command};
use crate::timings::Timer;
@@ -34,25 +35,32 @@ static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse);
/// Entry point.
fn main() -> ExitCode {
- let timer = Timer::new(&ARGS);
-
- let res = match &ARGS.command {
- Command::Compile(command) => crate::compile::compile(timer, command.clone()),
- Command::Watch(command) => crate::watch::watch(timer, command.clone()),
- Command::Init(command) => crate::init::init(command),
- Command::Query(command) => crate::query::query(command),
- Command::Fonts(command) => crate::fonts::fonts(command),
- Command::Update(command) => crate::update::update(command),
- };
+ let res = dispatch();
if let Err(msg) = res {
set_failed();
- print_error(&msg).expect("failed to print error");
+ print_error(msg.message()).expect("failed to print error");
}
EXIT.with(|cell| cell.get())
}
+/// Execute the requested command.
+fn dispatch() -> HintedStrResult<()> {
+ let timer = Timer::new(&ARGS);
+
+ match &ARGS.command {
+ Command::Compile(command) => crate::compile::compile(timer, command.clone())?,
+ Command::Watch(command) => crate::watch::watch(timer, command.clone())?,
+ Command::Init(command) => crate::init::init(command)?,
+ Command::Query(command) => crate::query::query(command)?,
+ Command::Fonts(command) => crate::fonts::fonts(command)?,
+ Command::Update(command) => crate::update::update(command)?,
+ }
+
+ Ok(())
+}
+
/// Ensure a failure exit code.
fn set_failed() {
EXIT.with(|cell| cell.set(ExitCode::FAILURE));
diff --git a/crates/typst-cli/src/query.rs b/crates/typst-cli/src/query.rs
index 6422af94..2e8bf7f8 100644
--- a/crates/typst-cli/src/query.rs
+++ b/crates/typst-cli/src/query.rs
@@ -1,7 +1,7 @@
use comemo::Track;
use ecow::{eco_format, EcoString};
use serde::Serialize;
-use typst::diag::{bail, StrResult};
+use typst::diag::{bail, HintedStrResult, StrResult};
use typst::eval::{eval_string, EvalMode, Tracer};
use typst::foundations::{Content, IntoValue, LocatableSelector, Scope};
use typst::model::Document;
@@ -14,7 +14,7 @@ use crate::set_failed;
use crate::world::SystemWorld;
/// Execute a query command.
-pub fn query(command: &QueryCommand) -> StrResult<()> {
+pub fn query(command: &QueryCommand) -> HintedStrResult<()> {
let mut world = SystemWorld::new(&command.common)?;
// Reset everything and ensure that the main file is present.
@@ -56,7 +56,7 @@ fn retrieve(
world: &dyn World,
command: &QueryCommand,
document: &Document,
-) -> StrResult<Vec<Content>> {
+) -> HintedStrResult<Vec<Content>> {
let selector = eval_string(
world.track(),
&command.selector,
diff --git a/crates/typst-macros/src/cast.rs b/crates/typst-macros/src/cast.rs
index f2115aa9..c732ce6b 100644
--- a/crates/typst-macros/src/cast.rs
+++ b/crates/typst-macros/src/cast.rs
@@ -108,7 +108,7 @@ pub fn cast(stream: TokenStream) -> Result<TokenStream> {
let from_value = (!input.from_value.is_empty() || input.dynamic).then(|| {
quote! {
impl #foundations::FromValue for #ty {
- fn from_value(value: #foundations::Value) -> ::typst::diag::StrResult<Self> {
+ fn from_value(value: #foundations::Value) -> ::typst::diag::HintedStrResult<Self> {
#from_value_body
}
}
diff --git a/crates/typst/src/diag.rs b/crates/typst/src/diag.rs
index 6e119591..15e6a49f 100644
--- a/crates/typst/src/diag.rs
+++ b/crates/typst/src/diag.rs
@@ -38,9 +38,14 @@ use crate::{World, WorldExt};
#[doc(hidden)]
macro_rules! __bail {
// For bail!("just a {}", "string")
- ($fmt:literal $(, $arg:expr)* $(,)?) => {
+ (
+ $fmt:literal $(, $arg:expr)*
+ $(; hint: $hint:literal $(, $hint_arg:expr)*)*
+ $(,)?
+ ) => {
return Err($crate::diag::error!(
- $fmt, $($arg),*
+ $fmt $(, $arg)*
+ $(; hint: $hint $(, $hint_arg)*)*
))
};
@@ -55,13 +60,25 @@ macro_rules! __bail {
};
}
-/// Construct an [`EcoString`] or [`SourceDiagnostic`] with severity `Error`.
+/// Construct an [`EcoString`], [`HintedString`] or [`SourceDiagnostic`] with
+/// severity `Error`.
#[macro_export]
#[doc(hidden)]
macro_rules! __error {
// For bail!("just a {}", "string").
($fmt:literal $(, $arg:expr)* $(,)?) => {
- $crate::diag::eco_format!($fmt, $($arg),*)
+ $crate::diag::eco_format!($fmt, $($arg),*).into()
+ };
+
+ // For bail!("a hinted {}", "string"; hint: "some hint"; hint: "...")
+ (
+ $fmt:literal $(, $arg:expr)*
+ $(; hint: $hint:literal $(, $hint_arg:expr)*)*
+ $(,)?
+ ) => {
+ $crate::diag::HintedString::new(
+ $crate::diag::eco_format!($fmt, $($arg),*)
+ ) $(.with_hint($crate::diag::eco_format!($hint, $($hint_arg),*)))*
};
// For bail!(span, ...)
@@ -296,13 +313,48 @@ where
pub type HintedStrResult<T> = Result<T, HintedString>;
/// A string message with hints.
+///
+/// This is internally represented by a vector of strings.
+/// The first element of the vector contains the message.
+/// The remaining elements are the hints.
+/// This is done to reduce the size of a HintedString.
+/// The vector is guaranteed to not be empty.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct HintedString {
+pub struct HintedString(EcoVec<EcoString>);
+
+impl HintedString {
+ /// Creates a new hinted string with the given message.
+ pub fn new(message: EcoString) -> Self {
+ Self(eco_vec![message])
+ }
+
/// A diagnostic message describing the problem.
- pub message: EcoString,
+ pub fn message(&self) -> &EcoString {
+ self.0.first().unwrap()
+ }
+
/// Additional hints to the user, indicating how this error could be avoided
/// or worked around.
- pub hints: Vec<EcoString>,
+ pub fn hints(&self) -> &[EcoString] {
+ self.0.get(1..).unwrap_or(&[])
+ }
+
+ /// Adds a single hint to the hinted string.
+ pub fn hint(&mut self, hint: impl Into<EcoString>) {
+ self.0.push(hint.into());
+ }
+
+ /// Adds a single hint to the hinted string.
+ pub fn with_hint(mut self, hint: impl Into<EcoString>) -> Self {
+ self.hint(hint);
+ self
+ }
+
+ /// Adds user-facing hints to the hinted string.
+ pub fn with_hints(mut self, hints: impl IntoIterator<Item = EcoString>) -> Self {
+ self.0.extend(hints);
+ self
+ }
}
impl<S> From<S> for HintedString
@@ -310,14 +362,18 @@ where
S: Into<EcoString>,
{
fn from(value: S) -> Self {
- Self { message: value.into(), hints: vec![] }
+ Self::new(value.into())
}
}
impl<T> At<T> for Result<T, HintedString> {
fn at(self, span: Span) -> SourceResult<T> {
- self.map_err(|diags| {
- eco_vec![SourceDiagnostic::error(span, diags.message).with_hints(diags.hints)]
+ self.map_err(|err| {
+ let mut components = err.0.into_iter();
+ let message = components.next().unwrap();
+ let diag = SourceDiagnostic::error(span, message).with_hints(components);
+
+ eco_vec![diag]
})
}
}
@@ -333,17 +389,14 @@ where
S: Into<EcoString>,
{
fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> {
- self.map_err(|message| HintedString {
- message: message.into(),
- hints: vec![hint.into()],
- })
+ self.map_err(|message| HintedString::new(message.into()).with_hint(hint))
}
}
impl<T> Hint<T> for HintedStrResult<T> {
fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> {
self.map_err(|mut error| {
- error.hints.push(hint.into());
+ error.hint(hint.into());
error
})
}
diff --git a/crates/typst/src/eval/code.rs b/crates/typst/src/eval/code.rs
index 30f5fb72..8542c846 100644
--- a/crates/typst/src/eval/code.rs
+++ b/crates/typst/src/eval/code.rs
@@ -1,6 +1,6 @@
use ecow::{eco_vec, EcoVec};
-use crate::diag::{bail, error, At, SourceDiagnostic, SourceResult};
+use crate::diag::{bail, error, At, SourceResult};
use crate::eval::{ops, CapturesVisitor, Eval, Vm};
use crate::foundations::{
Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement, Str, Value,
@@ -244,11 +244,11 @@ impl Eval for ast::Dict<'_> {
ast::DictItem::Keyed(keyed) => {
let raw_key = keyed.key();
let key = raw_key.eval(vm)?;
- let key = key.cast::<Str>().unwrap_or_else(|error| {
- let error = SourceDiagnostic::error(raw_key.span(), error);
- invalid_keys.push(error);
- Str::default()
- });
+ let key =
+ key.cast::<Str>().at(raw_key.span()).unwrap_or_else(|errors| {
+ invalid_keys.extend(errors);
+ Str::default()
+ });
map.insert(key, keyed.expr().eval(vm)?);
}
ast::DictItem::Spread(spread) => match spread.expr().eval(vm)? {
diff --git a/crates/typst/src/eval/ops.rs b/crates/typst/src/eval/ops.rs
index 13e64120..c9c8a24e 100644
--- a/crates/typst/src/eval/ops.rs
+++ b/crates/typst/src/eval/ops.rs
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
use ecow::eco_format;
-use crate::diag::{bail, At, SourceResult, StrResult};
+use crate::diag::{bail, At, HintedStrResult, SourceResult, StrResult};
use crate::eval::{access_dict, Access, Eval, Vm};
use crate::foundations::{format_str, Datetime, IntoValue, Regex, Repr, Value};
use crate::layout::{Alignment, Length, Rel};
@@ -59,7 +59,7 @@ impl Eval for ast::Binary<'_> {
fn apply_binary(
binary: ast::Binary,
vm: &mut Vm,
- op: fn(Value, Value) -> StrResult<Value>,
+ op: fn(Value, Value) -> HintedStrResult<Value>,
) -> SourceResult<Value> {
let lhs = binary.lhs().eval(vm)?;
@@ -78,7 +78,7 @@ fn apply_binary(
fn apply_assignment(
binary: ast::Binary,
vm: &mut Vm,
- op: fn(Value, Value) -> StrResult<Value>,
+ op: fn(Value, Value) -> HintedStrResult<Value>,
) -> SourceResult<Value> {
let rhs = binary.rhs().eval(vm)?;
let lhs = binary.lhs();
@@ -102,7 +102,7 @@ fn apply_assignment(
/// Bail with a type mismatch error.
macro_rules! mismatch {
($fmt:expr, $($value:expr),* $(,)?) => {
- return Err(eco_format!($fmt, $($value.ty()),*))
+ return Err(eco_format!($fmt, $($value.ty()),*).into())
};
}
@@ -134,7 +134,7 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Apply the unary plus operator to a value.
-pub fn pos(value: Value) -> StrResult<Value> {
+pub fn pos(value: Value) -> HintedStrResult<Value> {
use Value::*;
Ok(match value {
Int(v) => Int(v),
@@ -159,7 +159,7 @@ pub fn pos(value: Value) -> StrResult<Value> {
}
/// Compute the negation of a value.
-pub fn neg(value: Value) -> StrResult<Value> {
+pub fn neg(value: Value) -> HintedStrResult<Value> {
use Value::*;
Ok(match value {
Int(v) => Int(v.checked_neg().ok_or_else(too_large)?),
@@ -176,7 +176,7 @@ pub fn neg(value: Value) -> StrResult<Value> {
}
/// Compute the sum of two values.
-pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
use Value::*;
Ok(match (lhs, rhs) {
(a, None) => a,
@@ -252,7 +252,7 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Compute the difference of two values.
-pub fn sub(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn sub(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
use Value::*;
Ok(match (lhs, rhs) {
(Int(a), Int(b)) => Int(a.checked_sub(b).ok_or_else(too_large)?),
@@ -285,7 +285,7 @@ pub fn sub(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Compute the product of two values.
-pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn mul(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
use Value::*;
Ok(match (lhs, rhs) {
(Int(a), Int(b)) => Int(a.checked_mul(b).ok_or_else(too_large)?),
@@ -344,7 +344,7 @@ pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Compute the quotient of two values.
-pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn div(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
use Value::*;
if is_zero(&rhs) {
bail!("cannot divide by zero");
@@ -416,7 +416,7 @@ fn try_div_relative(a: Rel<Length>, b: Rel<Length>) -> StrResult<f64> {
}
/// Compute the logical "not" of a value.
-pub fn not(value: Value) -> StrResult<Value> {
+pub fn not(value: Value) -> HintedStrResult<Value> {
match value {
Value::Bool(b) => Ok(Value::Bool(!b)),
v => mismatch!("cannot apply 'not' to {}", v),
@@ -424,7 +424,7 @@ pub fn not(value: Value) -> StrResult<Value> {
}
/// Compute the logical "and" of two values.
-pub fn and(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn and(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
match (lhs, rhs) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a && b)),
(a, b) => mismatch!("cannot apply 'and' to {} and {}", a, b),
@@ -432,7 +432,7 @@ pub fn and(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Compute the logical "or" of two values.
-pub fn or(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn or(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
match (lhs, rhs) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a || b)),
(a, b) => mismatch!("cannot apply 'or' to {} and {}", a, b),
@@ -440,19 +440,19 @@ pub fn or(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Compute whether two values are equal.
-pub fn eq(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn eq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
Ok(Value::Bool(equal(&lhs, &rhs)))
}
/// Compute whether two values are unequal.
-pub fn neq(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn neq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
Ok(Value::Bool(!equal(&lhs, &rhs)))
}
macro_rules! comparison {
($name:ident, $op:tt, $($pat:tt)*) => {
/// Compute how a value compares with another value.
- pub fn $name(lhs: Value, rhs: Value) -> StrResult<Value> {
+ pub fn $name(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
let ordering = compare(&lhs, &rhs)?;
Ok(Value::Bool(matches!(ordering, $($pat)*)))
}
@@ -577,7 +577,7 @@ fn try_cmp_arrays(a: &[Value], b: &[Value]) -> StrResult<Ordering> {
}
/// Test whether one value is "in" another one.
-pub fn in_(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn in_(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
if let Some(b) = contains(&lhs, &rhs) {
Ok(Value::Bool(b))
} else {
@@ -586,7 +586,7 @@ pub fn in_(lhs: Value, rhs: Value) -> StrResult<Value> {
}
/// Test whether one value is "not in" another one.
-pub fn not_in(lhs: Value, rhs: Value) -> StrResult<Value> {
+pub fn not_in(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
if let Some(b) = contains(&lhs, &rhs) {
Ok(Value::Bool(!b))
} else {
diff --git a/crates/typst/src/foundations/args.rs b/crates/typst/src/foundations/args.rs
index 4624ae02..c59e4985 100644
--- a/crates/typst/src/foundations/args.rs
+++ b/crates/typst/src/foundations/args.rs
@@ -178,9 +178,9 @@ impl Args {
};
let span = item.value.span;
let spanned = Spanned::new(std::mem::take(&mut item.value.v), span);
- match T::from_value(spanned) {
+ match T::from_value(spanned).at(span) {
Ok(val) => list.push(val),
- Err(err) => errors.push(SourceDiagnostic::error(span, err)),
+ Err(diags) => errors.extend(diags),
}
false
});
diff --git a/crates/typst/src/foundations/array.rs b/crates/typst/src/foundations/array.rs
index 2f65800e..2caefc13 100644
--- a/crates/typst/src/foundations/array.rs
+++ b/crates/typst/src/foundations/array.rs
@@ -8,7 +8,7 @@ use ecow::{eco_format, EcoString, EcoVec};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
-use crate::diag::{bail, At, SourceDiagnostic, SourceResult, StrResult};
+use crate::diag::{bail, At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult};
use crate::engine::Engine;
use crate::eval::ops;
use crate::foundations::{
@@ -594,7 +594,7 @@ impl Array {
/// be empty.
#[named]
default: Option<Value>,
- ) -> StrResult<Value> {
+ ) -> HintedStrResult<Value> {
let mut iter = self.into_iter();
let mut acc = iter
.next()
@@ -615,7 +615,7 @@ impl Array {
/// be empty.
#[named]
default: Option<Value>,
- ) -> StrResult<Value> {
+ ) -> HintedStrResult<Value> {
let mut iter = self.into_iter();
let mut acc = iter
.next()
@@ -1095,13 +1095,13 @@ impl<T: IntoValue, const N: usize> IntoValue for SmallVec<[T; N]> {
}
impl<T: FromValue> FromValue for Vec<T> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
value.cast::<Array>()?.into_iter().map(Value::cast).collect()
}
}
impl<T: FromValue, const N: usize> FromValue for SmallVec<[T; N]> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
value.cast::<Array>()?.into_iter().map(Value::cast).collect()
}
}
diff --git a/crates/typst/src/foundations/auto.rs b/crates/typst/src/foundations/auto.rs
index 5711b1f6..cb9d999e 100644
--- a/crates/typst/src/foundations/auto.rs
+++ b/crates/typst/src/foundations/auto.rs
@@ -1,7 +1,7 @@
use ecow::EcoString;
use std::fmt::{self, Debug, Formatter};
-use crate::diag::StrResult;
+use crate::diag::HintedStrResult;
use crate::foundations::{
ty, CastInfo, Fold, FromValue, IntoValue, Reflect, Repr, Resolve, StyleChain, Type,
Value,
@@ -26,7 +26,7 @@ impl IntoValue for AutoValue {
}
impl FromValue for AutoValue {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::Auto => Ok(Self),
_ => Err(Self::error(&value)),
@@ -236,7 +236,7 @@ impl<T: IntoValue> IntoValue for Smart<T> {
}
impl<T: FromValue> FromValue for Smart<T> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::Auto => Ok(Self::Auto),
v if T::castable(&v) => Ok(Self::Custom(T::from_value(v)?)),
diff --git a/crates/typst/src/foundations/cast.rs b/crates/typst/src/foundations/cast.rs
index b29e4473..b53576c2 100644
--- a/crates/typst/src/foundations/cast.rs
+++ b/crates/typst/src/foundations/cast.rs
@@ -7,7 +7,7 @@ use ecow::{eco_format, EcoString};
use smallvec::SmallVec;
use unicode_math_class::MathClass;
-use crate::diag::{At, HintedStrResult, SourceResult, StrResult};
+use crate::diag::{At, HintedStrResult, HintedString, SourceResult, StrResult};
use crate::foundations::{array, repr, NativeElement, Packed, Repr, Str, Type, Value};
use crate::syntax::{Span, Spanned};
@@ -50,8 +50,8 @@ pub trait Reflect {
/// "expected integer, found none",
/// );
/// ```
- fn error(found: &Value) -> EcoString {
- Self::input().error(found)
+ fn error(found: &Value) -> HintedString {
+ Self::input().error(found).into()
}
}
@@ -249,17 +249,17 @@ impl<T: IntoValue> IntoValue for fn() -> T {
/// See also: [`Reflect`].
pub trait FromValue<V = Value>: Sized + Reflect {
/// Try to cast the value into an instance of `Self`.
- fn from_value(value: V) -> StrResult<Self>;
+ fn from_value(value: V) -> HintedStrResult<Self>;
}
impl FromValue for Value {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
Ok(value)
}
}
impl<T: NativeElement + FromValue> FromValue for Packed<T> {
- fn from_value(mut value: Value) -> StrResult<Self> {
+ fn from_value(mut value: Value) -> HintedStrResult<Self> {
if let Value::Content(content) = value {
match content.into_packed::<T>() {
Ok(packed) => return Ok(packed),
@@ -272,13 +272,13 @@ impl<T: NativeElement + FromValue> FromValue for Packed<T> {
}
impl<T: FromValue> FromValue<Spanned<Value>> for T {
- fn from_value(value: Spanned<Value>) -> StrResult<Self> {
+ fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
T::from_value(value.v)
}
}
impl<T: FromValue> FromValue<Spanned<Value>> for Spanned<T> {
- fn from_value(value: Spanned<Value>) -> StrResult<Self> {
+ fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
let span = value.span;
T::from_value(value.v).map(|t| Spanned::new(t, span))
}
@@ -432,7 +432,7 @@ impl IntoValue for Never {
}
impl FromValue for Never {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
Err(Self::error(&value))
}
}
diff --git a/crates/typst/src/foundations/none.rs b/crates/typst/src/foundations/none.rs
index afb055a6..d376c0c5 100644
--- a/crates/typst/src/foundations/none.rs
+++ b/crates/typst/src/foundations/none.rs
@@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
use ecow::EcoString;
use serde::{Serialize, Serializer};
-use crate::diag::StrResult;
+use crate::diag::HintedStrResult;
use crate::foundations::{
cast, ty, CastInfo, FromValue, IntoValue, Reflect, Repr, Type, Value,
};
@@ -45,7 +45,7 @@ impl IntoValue for NoneValue {
}
impl FromValue for NoneValue {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::None => Ok(Self),
_ => Err(Self::error(&value)),
@@ -104,7 +104,7 @@ impl<T: IntoValue> IntoValue for Option<T> {
}
impl<T: FromValue> FromValue for Option<T> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::None => Ok(None),
v if T::castable(&v) => Ok(Some(T::from_value(v)?)),
diff --git a/crates/typst/src/foundations/scope.rs b/crates/typst/src/foundations/scope.rs
index 0cbbe923..e0ef7a72 100644
--- a/crates/typst/src/foundations/scope.rs
+++ b/crates/typst/src/foundations/scope.rs
@@ -97,17 +97,14 @@ fn cannot_mutate_constant(var: &str) -> HintedString {
/// The error message when a variable is not found.
#[cold]
fn unknown_variable(var: &str) -> HintedString {
- let mut res = HintedString {
- message: eco_format!("unknown variable: {}", var),
- hints: vec![],
- };
+ let mut res = HintedString::new(eco_format!("unknown variable: {}", var));
if matches!(var, "none" | "auto" | "false" | "true") {
- res.hints.push(eco_format!(
+ res.hint(eco_format!(
"if you meant to use a literal, try adding a hash before it"
));
} else if var.contains('-') {
- res.hints.push(eco_format!(
+ res.hint(eco_format!(
"if you meant to use subtraction, try adding spaces around the minus sign",
));
}
diff --git a/crates/typst/src/foundations/selector.rs b/crates/typst/src/foundations/selector.rs
index d41fd2dc..1aee961b 100644
--- a/crates/typst/src/foundations/selector.rs
+++ b/crates/typst/src/foundations/selector.rs
@@ -339,7 +339,7 @@ cast! {
}
impl FromValue for LocatableSelector {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
fn validate(selector: &Selector) -> StrResult<()> {
match selector {
Selector::Elem(elem, _) => {
@@ -421,8 +421,8 @@ cast! {
}
impl FromValue for ShowableSelector {
- fn from_value(value: Value) -> StrResult<Self> {
- fn validate(selector: &Selector, nested: bool) -> StrResult<()> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
+ fn validate(selector: &Selector, nested: bool) -> HintedStrResult<()> {
match selector {
Selector::Elem(_, _) => {}
Selector::Label(_) => {}
diff --git a/crates/typst/src/foundations/value.rs b/crates/typst/src/foundations/value.rs
index 5d99a586..d2190ae7 100644
--- a/crates/typst/src/foundations/value.rs
+++ b/crates/typst/src/foundations/value.rs
@@ -9,7 +9,7 @@ use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
use serde::de::{Error, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use crate::diag::StrResult;
+use crate::diag::{HintedStrResult, HintedString, StrResult};
use crate::eval::ops;
use crate::foundations::{
fields, repr, Args, Array, AutoValue, Bytes, CastInfo, Content, Datetime, Dict,
@@ -151,7 +151,7 @@ impl Value {
}
/// Try to cast the value into a specific type.
- pub fn cast<T: FromValue>(self) -> StrResult<T> {
+ pub fn cast<T: FromValue>(self) -> HintedStrResult<T> {
T::from_value(self)
}
@@ -606,7 +606,7 @@ macro_rules! primitive {
}
impl FromValue for $ty {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::$variant(v) => Ok(v),
$(Value::$other$(($binding))? => Ok($out),)*
@@ -614,7 +614,7 @@ macro_rules! primitive {
"expected {}, found {}",
Type::of::<Self>(),
v.ty(),
- )),
+ ).into()),
}
}
}
@@ -682,7 +682,7 @@ impl<T: Reflect> Reflect for Arc<T> {
T::castable(value)
}
- fn error(found: &Value) -> EcoString {
+ fn error(found: &Value) -> HintedString {
T::error(found)
}
}
@@ -694,7 +694,7 @@ impl<T: Clone + IntoValue> IntoValue for Arc<T> {
}
impl<T: FromValue> FromValue for Arc<T> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
v if T::castable(&v) => Ok(Arc::new(T::from_value(v)?)),
_ => Err(Self::error(&value)),
diff --git a/crates/typst/src/introspection/counter.rs b/crates/typst/src/introspection/counter.rs
index 2365b3e8..2a7e9ea7 100644
--- a/crates/typst/src/introspection/counter.rs
+++ b/crates/typst/src/introspection/counter.rs
@@ -5,7 +5,7 @@ use comemo::{Track, Tracked, TrackedMut};
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
use smallvec::{smallvec, SmallVec};
-use crate::diag::{bail, At, SourceResult, StrResult};
+use crate::diag::{bail, At, HintedStrResult, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{
@@ -709,7 +709,7 @@ cast! {
array: Array => Self(array
.into_iter()
.map(Value::cast)
- .collect::<StrResult<_>>()?),
+ .collect::<HintedStrResult<_>>()?),
}
/// Executes an update of a counter.
diff --git a/crates/typst/src/layout/align.rs b/crates/typst/src/layout/align.rs
index 18879692..1bd9e970 100644
--- a/crates/typst/src/layout/align.rs
+++ b/crates/typst/src/layout/align.rs
@@ -2,7 +2,7 @@ use std::ops::Add;
use ecow::{eco_format, EcoString};
-use crate::diag::{bail, SourceResult, StrResult};
+use crate::diag::{bail, HintedStrResult, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, func, scope, ty, CastInfo, Content, Fold, FromValue, IntoValue, Packed,
@@ -637,7 +637,7 @@ where
H: Reflect + TryFrom<Alignment, Error = EcoString>,
V: Reflect + TryFrom<Alignment, Error = EcoString>,
{
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
if Alignment::castable(&value) {
let align = Alignment::from_value(value)?;
let result = match align {
diff --git a/crates/typst/src/layout/corners.rs b/crates/typst/src/layout/corners.rs
index 4b90854a..a3b6ba93 100644
--- a/crates/typst/src/layout/corners.rs
+++ b/crates/typst/src/layout/corners.rs
@@ -1,6 +1,6 @@
use std::fmt::{self, Debug, Formatter};
-use crate::diag::StrResult;
+use crate::diag::HintedStrResult;
use crate::foundations::{
AlternativeFold, CastInfo, Dict, Fold, FromValue, IntoValue, Reflect, Resolve,
StyleChain, Value,
@@ -175,7 +175,7 @@ impl<T> FromValue for Corners<Option<T>>
where
T: FromValue + Clone,
{
- fn from_value(mut value: Value) -> StrResult<Self> {
+ fn from_value(mut value: Value) -> HintedStrResult<Self> {
let expected_keys = [
"top-left",
"top-right",
@@ -224,7 +224,7 @@ where
let keys = dict.iter().map(|kv| kv.0.as_str()).collect();
// Do not hint at expected_keys, because T may be castable from Dict
// objects with other sets of expected keys.
- Err(Dict::unexpected_keys(keys, None))
+ Err(Dict::unexpected_keys(keys, None).into())
} else {
Err(Self::error(&value))
}
diff --git a/crates/typst/src/layout/grid/cells.rs b/crates/typst/src/layout/grid/cells.rs
index 8ec84bc3..efa7bb6e 100644
--- a/crates/typst/src/layout/grid/cells.rs
+++ b/crates/typst/src/layout/grid/cells.rs
@@ -6,9 +6,7 @@ use ecow::eco_format;
use super::lines::Line;
use super::repeated::{Footer, Header, Repeatable};
-use crate::diag::{
- bail, At, Hint, HintedStrResult, HintedString, SourceResult, StrResult,
-};
+use crate::diag::{bail, At, Hint, HintedStrResult, HintedString, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
Array, CastInfo, Content, Context, Fold, FromValue, Func, IntoValue, Reflect,
@@ -85,11 +83,11 @@ impl<T: IntoValue> IntoValue for Celled<T> {
}
impl<T: FromValue> FromValue for Celled<T> {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
match value {
Value::Func(v) => Ok(Self::Func(v)),
Value::Array(array) => Ok(Self::Array(
- array.into_iter().map(T::from_value).collect::<StrResult<_>>()?,
+ array.into_iter().map(T::from_value).collect::<HintedStrResult<_>>()?,
)),
v if T::castable(&v) => Ok(Self::Value(T::from_value(v)?)),
v => Err(Self::error(&v)),
diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs
index e1fec58e..57e28730 100644
--- a/crates/typst/src/layout/grid/mod.rs
+++ b/crates/typst/src/layout/grid/mod.rs
@@ -13,10 +13,10 @@ pub use self::lines::LinePosition;
use std::num::NonZeroUsize;
use std::sync::Arc;
-use ecow::{eco_format, EcoString};
+use ecow::eco_format;
use smallvec::{smallvec, SmallVec};
-use crate::diag::{bail, SourceResult, StrResult, Trace, Tracepoint};
+use crate::diag::{bail, HintedStrResult, HintedString, SourceResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, Fold, NativeElement, Packed, Show, Smart,
@@ -406,7 +406,7 @@ cast! {
self => self.0.into_value(),
sizing: Sizing => Self(smallvec![sizing]),
count: NonZeroUsize => Self(smallvec![Sizing::Auto; count.get()]),
- values: Array => Self(values.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ values: Array => Self(values.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
/// Any child of a grid element.
@@ -430,13 +430,19 @@ cast! {
}
impl TryFrom<Content> for GridChild {
- type Error = EcoString;
- fn try_from(value: Content) -> StrResult<Self> {
+ type Error = HintedString;
+ fn try_from(value: Content) -> HintedStrResult<Self> {
if value.is::<TableHeader>() {
- bail!("cannot use `table.header` as a grid header; use `grid.header` instead")
+ bail!(
+ "cannot use `table.header` as a grid header";
+ hint: "use `grid.header` instead"
+ )
}
if value.is::<TableFooter>() {
- bail!("cannot use `table.footer` as a grid footer; use `grid.footer` instead")
+ bail!(
+ "cannot use `table.footer` as a grid footer";
+ hint: "use `grid.footer` instead"
+ )
}
value
@@ -506,8 +512,8 @@ cast! {
}
impl TryFrom<Content> for GridItem {
- type Error = EcoString;
- fn try_from(value: Content) -> StrResult<Self> {
+ type Error = HintedString;
+ fn try_from(value: Content) -> HintedStrResult<Self> {
if value.is::<GridHeader>() {
bail!("cannot place a grid header within another header or footer");
}
@@ -521,13 +527,22 @@ impl TryFrom<Content> for GridItem {
bail!("cannot place a table footer within another footer or header");
}
if value.is::<TableCell>() {
- bail!("cannot use `table.cell` as a grid cell; use `grid.cell` instead");
+ bail!(
+ "cannot use `table.cell` as a grid cell";
+ hint: "use `grid.cell` instead"
+ );
}
if value.is::<TableHLine>() {
- bail!("cannot use `table.hline` as a grid line; use `grid.hline` instead");
+ bail!(
+ "cannot use `table.hline` as a grid line";
+ hint: "use `grid.hline` instead"
+ );
}
if value.is::<TableVLine>() {
- bail!("cannot use `table.vline` as a grid line; use `grid.vline` instead");
+ bail!(
+ "cannot use `table.vline` as a grid line";
+ hint: "use `grid.vline` instead"
+ );
}
Ok(value
diff --git a/crates/typst/src/layout/sides.rs b/crates/typst/src/layout/sides.rs
index ddf58ea9..75d4e25b 100644
--- a/crates/typst/src/layout/sides.rs
+++ b/crates/typst/src/layout/sides.rs
@@ -1,7 +1,7 @@
use std::fmt::{self, Debug, Formatter};
use std::ops::Add;
-use crate::diag::{bail, StrResult};
+use crate::diag::{bail, HintedStrResult};
use crate::foundations::{
cast, AlternativeFold, CastInfo, Dict, Fold, FromValue, IntoValue, Reflect, Resolve,
StyleChain, Value,
@@ -209,7 +209,7 @@ impl<T> FromValue for Sides<Option<T>>
where
T: Default + FromValue + Clone,
{
- fn from_value(mut value: Value) -> StrResult<Self> {
+ fn from_value(mut value: Value) -> HintedStrResult<Self> {
let expected_keys = ["left", "top", "right", "bottom", "x", "y", "rest"];
if let Value::Dict(dict) = &mut value {
if dict.is_empty() {
@@ -237,7 +237,7 @@ where
let keys = dict.iter().map(|kv| kv.0.as_str()).collect();
// Do not hint at expected_keys, because T may be castable from Dict
// objects with other sets of expected keys.
- Err(Dict::unexpected_keys(keys, None))
+ Err(Dict::unexpected_keys(keys, None).into())
} else {
Err(Self::error(&value))
}
diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs
index 2581cfee..4dadd756 100644
--- a/crates/typst/src/math/matrix.rs
+++ b/crates/typst/src/math/matrix.rs
@@ -1,7 +1,7 @@
use smallvec::{smallvec, SmallVec};
use unicode_math_class::MathClass;
-use crate::diag::{bail, At, SourceResult, StrResult};
+use crate::diag::{bail, At, HintedStrResult, SourceResult, StrResult};
use crate::foundations::{
array, cast, dict, elem, Array, Content, Dict, Fold, NoneValue, Packed, Resolve,
Smart, StyleChain, Value,
@@ -712,5 +712,5 @@ cast! {
AugmentOffsets,
self => self.0.into_value(),
v: isize => Self(smallvec![v]),
- v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
diff --git a/crates/typst/src/model/bibliography.rs b/crates/typst/src/model/bibliography.rs
index c820bc40..57a33fc4 100644
--- a/crates/typst/src/model/bibliography.rs
+++ b/crates/typst/src/model/bibliography.rs
@@ -19,7 +19,7 @@ use once_cell::sync::Lazy;
use smallvec::{smallvec, SmallVec};
use typed_arena::Arena;
-use crate::diag::{bail, error, At, FileError, SourceResult, StrResult};
+use crate::diag::{bail, error, At, FileError, HintedStrResult, SourceResult, StrResult};
use crate::engine::Engine;
use crate::eval::{eval_string, EvalMode};
use crate::foundations::{
@@ -150,7 +150,7 @@ cast! {
BibliographyPaths,
self => self.0.into_value(),
v: EcoString => Self(vec![v]),
- v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
impl BibliographyElem {
@@ -508,7 +508,7 @@ impl Reflect for CslStyle {
}
impl FromValue for CslStyle {
- fn from_value(value: Value) -> StrResult<Self> {
+ fn from_value(value: Value) -> HintedStrResult<Self> {
if let Value::Dyn(dynamic) = &value {
if let Some(concrete) = dynamic.downcast::<Self>() {
return Ok(concrete.clone());
diff --git a/crates/typst/src/model/document.rs b/crates/typst/src/model/document.rs
index ebe21505..7bc517ee 100644
--- a/crates/typst/src/model/document.rs
+++ b/crates/typst/src/model/document.rs
@@ -1,6 +1,6 @@
use ecow::EcoString;
-use crate::diag::{bail, SourceResult, StrResult};
+use crate::diag::{bail, HintedStrResult, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Args, Array, Construct, Content, Datetime, Packed, Smart, StyleChain,
@@ -115,7 +115,7 @@ cast! {
Author,
self => self.0.into_value(),
v: EcoString => Self(vec![v]),
- v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
/// A list of keywords.
@@ -126,7 +126,7 @@ cast! {
Keywords,
self => self.0.into_value(),
v: EcoString => Self(vec![v]),
- v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
/// A finished document with metadata and page frames.
diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs
index 04a785b6..0c56b7e4 100644
--- a/crates/typst/src/model/table.rs
+++ b/crates/typst/src/model/table.rs
@@ -1,9 +1,9 @@
use std::num::NonZeroUsize;
use std::sync::Arc;
-use ecow::{eco_format, EcoString};
+use ecow::eco_format;
-use crate::diag::{bail, SourceResult, StrResult, Trace, Tracepoint};
+use crate::diag::{bail, HintedStrResult, HintedString, SourceResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Content, Fold, NativeElement, Packed, Show, Smart, StyleChain,
@@ -346,17 +346,19 @@ cast! {
}
impl TryFrom<Content> for TableChild {
- type Error = EcoString;
+ type Error = HintedString;
- fn try_from(value: Content) -> StrResult<Self> {
+ fn try_from(value: Content) -> HintedStrResult<Self> {
if value.is::<GridHeader>() {
bail!(
- "cannot use `grid.header` as a table header; use `table.header` instead"
+ "cannot use `grid.header` as a table header";
+ hint: "use `table.header` instead"
)
}
if value.is::<GridFooter>() {
bail!(
- "cannot use `grid.footer` as a table footer; use `table.footer` instead"
+ "cannot use `grid.footer` as a table footer";
+ hint: "use `table.footer` instead"
)
}
@@ -427,9 +429,9 @@ cast! {
}
impl TryFrom<Content> for TableItem {
- type Error = EcoString;
+ type Error = HintedString;
- fn try_from(value: Content) -> StrResult<Self> {
+ fn try_from(value: Content) -> HintedStrResult<Self> {
if value.is::<GridHeader>() {
bail!("cannot place a grid header within another header or footer");
}
@@ -443,13 +445,22 @@ impl TryFrom<Content> for TableItem {
bail!("cannot place a table footer within another footer or header");
}
if value.is::<GridCell>() {
- bail!("cannot use `grid.cell` as a table cell; use `table.cell` instead");
+ bail!(
+ "cannot use `grid.cell` as a table cell";
+ hint: "use `table.cell` instead"
+ );
}
if value.is::<GridHLine>() {
- bail!("cannot use `grid.hline` as a table line; use `table.hline` instead");
+ bail!(
+ "cannot use `grid.hline` as a table line";
+ hint: "use `table.hline` instead"
+ );
}
if value.is::<GridVLine>() {
- bail!("cannot use `grid.vline` as a table line; use `table.vline` instead");
+ bail!(
+ "cannot use `grid.vline` as a table line";
+ hint: "use `table.vline` instead"
+ );
}
Ok(value
diff --git a/crates/typst/src/text/lang.rs b/crates/typst/src/text/lang.rs
index 6809238a..05d6104e 100644
--- a/crates/typst/src/text/lang.rs
+++ b/crates/typst/src/text/lang.rs
@@ -1,7 +1,8 @@
use std::collections::HashMap;
use std::str::FromStr;
-use ecow::EcoString;
+use crate::diag::Hint;
+use ecow::{eco_format, EcoString};
use crate::foundations::{cast, StyleChain};
use crate::layout::Dir;
@@ -121,7 +122,22 @@ impl FromStr for Lang {
cast! {
Lang,
self => self.as_str().into_value(),
- string: EcoString => Self::from_str(&string)?,
+ string: EcoString => {
+ let result = Self::from_str(&string);
+ if result.is_err() {
+ if let Some((lang, region)) = string.split_once('-') {
+ if Lang::from_str(lang).is_ok() && Region::from_str(region).is_ok() {
+ return result
+ .hint(eco_format!(
+ "you should leave only \"{}\" in the `lang` parameter and specify \"{}\" in the `region` parameter",
+ lang, region,
+ ));
+ }
+ }
+ }
+
+ result?
+ }
}
/// An identifier for a region somewhere in the world.
diff --git a/crates/typst/src/text/mod.rs b/crates/typst/src/text/mod.rs
index df140419..4b10b807 100644
--- a/crates/typst/src/text/mod.rs
+++ b/crates/typst/src/text/mod.rs
@@ -29,14 +29,13 @@ pub use self::smartquote::*;
pub use self::space::*;
use std::fmt::{self, Debug, Formatter};
-use std::str::FromStr;
use ecow::{eco_format, EcoString};
use rustybuzz::{Feature, Tag};
use smallvec::SmallVec;
use ttf_parser::Rect;
-use crate::diag::{bail, warning, At, Hint, SourceResult, StrResult};
+use crate::diag::{bail, warning, HintedStrResult, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, category, dict, elem, Args, Array, Cast, Category, Construct, Content, Dict,
@@ -394,7 +393,6 @@ pub struct TextElem {
/// = Einleitung
/// In diesem Dokument, ...
/// ```
- #[parse(parse_lang(args)?)]
#[default(Lang::ENGLISH)]
#[ghost]
pub lang: Lang,
@@ -812,7 +810,7 @@ cast! {
self.0.into_value()
},
family: FontFamily => Self(vec![family]),
- values: Array => Self(values.into_iter().map(|v| v.cast()).collect::<StrResult<_>>()?),
+ values: Array => Self(values.into_iter().map(|v| v.cast()).collect::<HintedStrResult<_>>()?),
}
/// Resolve a prioritized iterator over the font families.
@@ -1131,7 +1129,7 @@ cast! {
let tag = v.cast::<EcoString>()?;
Ok((Tag::from_bytes_lossy(tag.as_bytes()), 1))
})
- .collect::<StrResult<_>>()?),
+ .collect::<HintedStrResult<_>>()?),
values: Dict => Self(values
.into_iter()
.map(|(k, v)| {
@@ -1139,7 +1137,7 @@ cast! {
let tag = Tag::from_bytes_lossy(k.as_bytes());
Ok((tag, num))
})
- .collect::<StrResult<_>>()?),
+ .collect::<HintedStrResult<_>>()?),
}
impl Fold for FontFeatures {
@@ -1300,27 +1298,3 @@ cast! {
ret
},
}
-
-/// Function to parse the language argument.
-/// Provides a hint if a region is used in the language parameter.
-fn parse_lang(args: &mut Args) -> SourceResult<Option<Lang>> {
- let Some(Spanned { v: iso, span }) = args.named::<Spanned<EcoString>>("lang")? else {
- return Ok(None);
- };
-
- let result = Lang::from_str(&iso);
- if result.is_err() {
- if let Some((lang, region)) = iso.split_once('-') {
- if Lang::from_str(lang).is_ok() && Region::from_str(region).is_ok() {
- return result
- .hint(eco_format!(
- "you should leave only \"{}\" in the `lang` parameter and specify \"{}\" in the `region` parameter",
- lang, region,
- ))
- .at(span)
- .map(Some);
- }
- }
- }
- result.at(span).map(Some)
-}
diff --git a/crates/typst/src/text/raw.rs b/crates/typst/src/text/raw.rs
index 6f900927..e8cb3b99 100644
--- a/crates/typst/src/text/raw.rs
+++ b/crates/typst/src/text/raw.rs
@@ -10,7 +10,7 @@ use syntect::parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder};
use unicode_segmentation::UnicodeSegmentation;
use super::Lang;
-use crate::diag::{At, FileError, SourceResult, StrResult};
+use crate::diag::{At, FileError, HintedStrResult, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Args, Array, Bytes, Content, Fold, NativeElement, Packed,
@@ -728,7 +728,7 @@ cast! {
SyntaxPaths,
self => self.0.into_value(),
v: EcoString => Self(vec![v]),
- v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
}
impl Fold for SyntaxPaths {
diff --git a/crates/typst/src/text/smartquote.rs b/crates/typst/src/text/smartquote.rs
index 4bb5ca01..236d0636 100644
--- a/crates/typst/src/text/smartquote.rs
+++ b/crates/typst/src/text/smartquote.rs
@@ -1,7 +1,7 @@
use ecow::EcoString;
use unicode_segmentation::UnicodeSegmentation;
-use crate::diag::{bail, StrResult};
+use crate::diag::{bail, HintedStrResult, StrResult};
use crate::foundations::{
array, cast, dict, elem, Array, Dict, FromValue, Packed, PlainText, Smart, Str,
};
@@ -332,7 +332,7 @@ fn str_to_set(value: &str) -> StrResult<[EcoString; 2]> {
}
}
-fn array_to_set(value: Array) -> StrResult<[EcoString; 2]> {
+fn array_to_set(value: Array) -> HintedStrResult<[EcoString; 2]> {
let value = value.as_slice();
if value.len() != 2 {
bail!(
diff --git a/crates/typst/src/visualize/stroke.rs b/crates/typst/src/visualize/stroke.rs
index 7d6909a5..234ad1da 100644
--- a/crates/typst/src/visualize/stroke.rs
+++ b/crates/typst/src/visualize/stroke.rs
@@ -1,6 +1,6 @@
use ecow::EcoString;
-use crate::diag::{SourceResult, StrResult};
+use crate::diag::{HintedStrResult, SourceResult};
use crate::foundations::{
cast, dict, func, scope, ty, Args, Cast, Dict, Fold, FromValue, NoneValue, Repr,
Resolve, Smart, StyleChain, Value,
@@ -378,7 +378,7 @@ cast! {
},
mut dict: Dict => {
// Get a value by key, accepting either Auto or something convertible to type T.
- fn take<T: FromValue>(dict: &mut Dict, key: &str) -> StrResult<Smart<T>> {
+ fn take<T: FromValue>(dict: &mut Dict, key: &str) -> HintedStrResult<Smart<T>> {
Ok(dict.take(key).ok().map(Smart::<T>::from_value)
.transpose()?.unwrap_or(Smart::Auto))
}
diff --git a/tests/suite/layout/grid/cell.typ b/tests/suite/layout/grid/cell.typ
index 3b08c752..cc526adb 100644
--- a/tests/suite/layout/grid/cell.typ
+++ b/tests/suite/layout/grid/cell.typ
@@ -128,5 +128,6 @@
}
--- table-cell-in-grid ---
-// Error: 7-19 cannot use `table.cell` as a grid cell; use `grid.cell` instead
+// Error: 7-19 cannot use `table.cell` as a grid cell
+// Hint: 7-19 use `grid.cell` instead
#grid(table.cell[])
diff --git a/tests/suite/layout/grid/footers.typ b/tests/suite/layout/grid/footers.typ
index c73bcb39..7cd09003 100644
--- a/tests/suite/layout/grid/footers.typ
+++ b/tests/suite/layout/grid/footers.typ
@@ -131,14 +131,16 @@
)
--- table-footer-in-grid ---
-// Error: 3:3-3:20 cannot use `table.footer` as a grid footer; use `grid.footer` instead
+// Error: 3:3-3:20 cannot use `table.footer` as a grid footer
+// Hint: 3:3-3:20 use `grid.footer` instead
#grid(
[a],
table.footer([a]),
)
--- grid-footer-in-table ---
-// Error: 3:3-3:19 cannot use `grid.footer` as a table footer; use `table.footer` instead
+// Error: 3:3-3:19 cannot use `grid.footer` as a table footer
+// Hint: 3:3-3:19 use `table.footer` instead
#table(
[a],
grid.footer([a]),
diff --git a/tests/suite/layout/grid/headers.typ b/tests/suite/layout/grid/headers.typ
index b9a90461..78a2540f 100644
--- a/tests/suite/layout/grid/headers.typ
+++ b/tests/suite/layout/grid/headers.typ
@@ -133,14 +133,16 @@
)
--- table-header-in-grid ---
-// Error: 2:3-2:20 cannot use `table.header` as a grid header; use `grid.header` instead
+// Error: 2:3-2:20 cannot use `table.header` as a grid header
+// Hint: 2:3-2:20 use `grid.header` instead
#grid(
table.header([a]),
[a],
)
--- grid-header-in-table ---
-// Error: 2:3-2:19 cannot use `grid.header` as a table header; use `table.header` instead
+// Error: 2:3-2:19 cannot use `grid.header` as a table header
+// Hint: 2:3-2:19 use `table.header` instead
#table(
grid.header([a]),
[a],
diff --git a/tests/suite/layout/grid/stroke.typ b/tests/suite/layout/grid/stroke.typ
index 9c1c3482..3a26b69a 100644
--- a/tests/suite/layout/grid/stroke.typ
+++ b/tests/suite/layout/grid/stroke.typ
@@ -385,19 +385,23 @@
)
--- table-hline-in-grid ---
-// Error: 7-20 cannot use `table.hline` as a grid line; use `grid.hline` instead
+// Error: 7-20 cannot use `table.hline` as a grid line
+// Hint: 7-20 use `grid.hline` instead
#grid(table.hline())
--- table-vline-in-grid ---
-// Error: 7-20 cannot use `table.vline` as a grid line; use `grid.vline` instead
+// Error: 7-20 cannot use `table.vline` as a grid line
+// Hint: 7-20 use `grid.vline` instead
#grid(table.vline())
--- grid-hline-in-table ---
-// Error: 8-20 cannot use `grid.hline` as a table line; use `table.hline` instead
+// Error: 8-20 cannot use `grid.hline` as a table line
+// Hint: 8-20 use `table.hline` instead
#table(grid.hline())
--- grid-vline-in-table ---
-// Error: 8-20 cannot use `grid.vline` as a table line; use `table.vline` instead
+// Error: 8-20 cannot use `grid.vline` as a table line
+// Hint: 8-20 use `table.vline` instead
#table(grid.vline())
--- grid-hline-end-before-start-1 ---
diff --git a/tests/suite/layout/table.typ b/tests/suite/layout/table.typ
index 7eec46a1..4090ef95 100644
--- a/tests/suite/layout/table.typ
+++ b/tests/suite/layout/table.typ
@@ -262,7 +262,8 @@
}
--- grid-cell-in-table ---
-// Error: 8-19 cannot use `grid.cell` as a table cell; use `table.cell` instead
+// Error: 8-19 cannot use `grid.cell` as a table cell
+// Hint: 8-19 use `table.cell` instead
#table(grid.cell[])
--- issue-183-table-lines ---