This commit is contained in:
Laurenz 2023-06-24 15:05:59 +02:00
parent b96b7b7ee1
commit f1025071ed
9 changed files with 63 additions and 63 deletions

View File

@ -303,15 +303,16 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> { fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
tracing::info!("Starting compilation"); tracing::info!("Starting compilation");
let start_time = std::time::Instant::now();
let start = std::time::Instant::now();
status(command, Status::Compiling).unwrap(); status(command, Status::Compiling).unwrap();
world.reset(); world.reset();
world.main = world.resolve(&command.input).map_err(|err| err.to_string())?; world.main = world.resolve(&command.input).map_err(|err| err.to_string())?;
let result = typst::compile(world); let result = typst::compile(world);
let duration = start_time.elapsed(); let duration = start.elapsed();
match result { match result {
// Export the PDF / PNG. // Export the PDF / PNG.
Ok(document) => { Ok(document) => {
@ -427,9 +428,9 @@ enum Status {
impl Status { impl Status {
fn message(&self) -> String { fn message(&self) -> String {
match self { match self {
Self::Compiling => "compiling ...".to_string(), Self::Compiling => "compiling ...".into(),
Self::Success(duration) => format!("compiled successfully in {duration:.2?}"), Self::Success(duration) => format!("compiled successfully in {duration:.2?}"),
Self::Error => "compiled with errors".to_string(), Self::Error => "compiled with errors".into(),
} }
} }

View File

@ -125,11 +125,10 @@ pub struct LimitsElem {
#[required] #[required]
pub body: Content, pub body: Content,
/// Whether to apply limits in inline equations. /// Whether to also force limits in inline equations.
/// ///
/// It is useful to disable this setting /// When applying limits globally (e.g., through a show rule), it is
/// in most cases of applying limits globally /// typically a good idea to disable this.
/// (inside show rules or new elements)
#[default(true)] #[default(true)]
pub inline: bool, pub inline: bool,
} }

View File

@ -85,11 +85,11 @@ impl MathRow {
self.0.iter() self.0.iter()
} }
/// It is very unintuitive, but in current state of things /// Extract the sublines of the row.
/// `MathRow` can contain several actual rows.
/// That function deconstructs it to "single" rows.
/// ///
/// Hopefully that cloner is only a temporary hack /// It is very unintuitive, but in current state of things, a `MathRow` can
/// contain several actual rows. That function deconstructs it to "single"
/// rows. Hopefully this is only a temporary hack.
pub fn rows(&self) -> Vec<Self> { pub fn rows(&self) -> Vec<Self> {
self.0 self.0
.split(|frag| matches!(frag, MathFragment::Linebreak)) .split(|frag| matches!(frag, MathFragment::Linebreak))

View File

@ -206,9 +206,8 @@ fn layout(
let body = body.into_fragment(ctx); let body = body.into_fragment(ctx);
let glyph = GlyphFragment::new(ctx, c, span); let glyph = GlyphFragment::new(ctx, c, span);
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());
let body = MathRow::new(vec![body]);
let mut rows = vec![body, stretched.into()]; let mut rows = vec![MathRow::new(vec![body]), stretched.into()];
ctx.style(if reverse { ctx.style(if reverse {
ctx.style.for_subscript() ctx.style.for_subscript()
} else { } else {

View File

@ -1,5 +1,3 @@
use typst::diag::Hint;
use super::{BibliographyElem, CiteElem, Counter, Figurable, Numbering}; use super::{BibliographyElem, CiteElem, Counter, Figurable, Numbering};
use crate::prelude::*; use crate::prelude::*;
use crate::text::TextElem; use crate::text::TextElem;

View File

@ -10,7 +10,7 @@ pub use comemo::{Track, Tracked, TrackedMut};
#[doc(no_inline)] #[doc(no_inline)]
pub use ecow::{eco_format, EcoString}; pub use ecow::{eco_format, EcoString};
#[doc(no_inline)] #[doc(no_inline)]
pub use typst::diag::{bail, error, At, SourceResult, StrResult}; pub use typst::diag::{bail, error, At, Hint, SourceResult, StrResult};
#[doc(no_inline)] #[doc(no_inline)]
pub use typst::doc::*; pub use typst::doc::*;
#[doc(no_inline)] #[doc(no_inline)]

View File

@ -82,7 +82,8 @@ pub struct SourceError {
pub message: EcoString, pub message: EcoString,
/// The trace of function calls leading to the error. /// The trace of function calls leading to the error.
pub trace: Vec<Spanned<Tracepoint>>, pub trace: Vec<Spanned<Tracepoint>>,
/// Additonal hints to the user, indicating how this error could be avoided or worked around. /// Additonal hints to the user, indicating how this error could be avoided
/// or worked around.
pub hints: Vec<EcoString>, pub hints: Vec<EcoString>,
} }
@ -104,6 +105,12 @@ impl SourceError {
self self
} }
/// Adds user-facing hints to the error.
pub fn with_hints(mut self, hints: impl IntoIterator<Item = EcoString>) -> Self {
self.hints.extend(hints);
self
}
/// The range in the source file identified by /// The range in the source file identified by
/// [`self.span.source()`](Span::source) where the error should be /// [`self.span.source()`](Span::source) where the error should be
/// annotated. /// annotated.
@ -115,12 +122,6 @@ impl SourceError {
ErrorPos::End => full.end..full.end, ErrorPos::End => full.end..full.end,
} }
} }
/// Adds a user-facing hint to the error.
pub fn with_hints(mut self, hints: &mut Vec<EcoString>) -> Self {
self.hints.append(hints);
self
}
} }
/// A part of an error's [trace](SourceError::trace). /// A part of an error's [trace](SourceError::trace).
@ -203,48 +204,44 @@ where
} }
} }
pub type StrWithHintResult<T> = Result<T, StrWithHint>; /// A result type with a string error message and hints.
pub type HintedStrResult<T> = Result<T, HintedString>;
pub struct StrWithHint { /// A string message with hints.
message: EcoString, #[derive(Debug, Clone, Eq, PartialEq, Hash)]
hints: Vec<EcoString>, pub struct HintedString {
/// A diagnostic message describing the problem.
pub message: EcoString,
/// Additonal hints to the user, indicating how this error could be avoided
/// or worked around.
pub hints: Vec<EcoString>,
} }
impl<T> At<T> for Result<T, StrWithHint> { impl<T> At<T> for Result<T, HintedString> {
fn at(self, span: Span) -> SourceResult<T> { fn at(self, span: Span) -> SourceResult<T> {
self.map_err(|mut diags| { self.map_err(|diags| {
Box::new(vec![ Box::new(vec![SourceError::new(span, diags.message).with_hints(diags.hints)])
SourceError::new(span, diags.message).with_hints(&mut diags.hints)
])
}) })
} }
} }
/// Allows adding a user-facing hint in addition to the error. /// Enrich a [`StrResult`] or [`HintedStrResult`] with a hint.
pub trait Hint<T, S> pub trait Hint<T> {
where /// Add the hint.
S: Into<EcoString>, fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T>;
{
fn hint(self, hint: S) -> StrWithHintResult<T>;
} }
impl<T, S> Hint<T, S> for StrResult<T> impl<T> Hint<T> for StrResult<T> {
where fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> {
S: Into<EcoString>, self.map_err(|message| HintedString { message, hints: vec![hint.into()] })
{
fn hint(self, hint: S) -> StrWithHintResult<T> {
self.map_err(|message| StrWithHint { message, hints: vec![hint.into()] })
} }
} }
impl<T, S> Hint<T, S> for StrWithHintResult<T> impl<T> Hint<T> for HintedStrResult<T> {
where fn hint(self, hint: impl Into<EcoString>) -> HintedStrResult<T> {
S: Into<EcoString>, self.map_err(|mut error| {
{ error.hints.push(hint.into());
fn hint(self, hint: S) -> StrWithHintResult<T> { error
self.map_err(|mut diags| {
diags.hints.push(hint.into());
diags
}) })
} }
} }

View File

@ -488,14 +488,18 @@ fn render_image(
let view_width = size.x.to_f32(); let view_width = size.x.to_f32();
let view_height = size.y.to_f32(); let view_height = size.y.to_f32();
let aspect = (image.width() as f32) / (image.height() as f32); // For better-looking output, resize `image` to its final size before
// For better-looking output, resize `image` to its final size before painting it to `canvas`. // painting it to `canvas`. For the math, see:
// See https://github.com/typst/typst/issues/1404#issuecomment-1598374652 for the math. // https://github.com/typst/typst/issues/1404#issuecomment-1598374652
let theta = f32::atan2(-ts.kx, ts.sx); let theta = f32::atan2(-ts.kx, ts.sx);
// To avoid division by 0, choose the one of { sin, cos } that is further from 0.
// To avoid division by 0, choose the one of { sin, cos } that is
// further from 0.
let prefer_sin = theta.sin().abs() > std::f32::consts::FRAC_1_SQRT_2; let prefer_sin = theta.sin().abs() > std::f32::consts::FRAC_1_SQRT_2;
let scale_x = let scale_x =
f32::abs(if prefer_sin { ts.kx / theta.sin() } else { ts.sx / theta.cos() }); f32::abs(if prefer_sin { ts.kx / theta.sin() } else { ts.sx / theta.cos() });
let aspect = (image.width() as f32) / (image.height() as f32);
let w = (scale_x * view_width.max(aspect * view_height)).ceil() as u32; let w = (scale_x * view_width.max(aspect * view_height)).ceil() as u32;
let h = ((w as f32) / aspect).ceil() as u32; let h = ((w as f32) / aspect).ceil() as u32;

View File

@ -2,13 +2,14 @@
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::Write; use std::fs;
use std::io::{self, Write};
use std::iter;
use std::ops::Range; use std::ops::Range;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{env, io};
use std::{fs, iter};
use clap::Parser; use clap::Parser;
use comemo::{Prehashed, Track}; use comemo::{Prehashed, Track};
@ -555,10 +556,11 @@ fn test_part(
} }
// Map errors to range and message format, discard traces and errors from // Map errors to range and message format, discard traces and errors from
// other files. Collect hints. // other files, collect hints.
// //
// This has one caveat: due to the format of the expected hints, we can not verify if a hint belongs // This has one caveat: due to the format of the expected hints, we can not
// to a error or not. That should be irrelevant however, as the line of the hint is still verified. // verify if a hint belongs to a error or not. That should be irrelevant
// however, as the line of the hint is still verified.
let actual_errors_and_hints: HashSet<UserOutput> = errors let actual_errors_and_hints: HashSet<UserOutput> = errors
.into_iter() .into_iter()
.filter(|error| error.span.source() == id) .filter(|error| error.span.source() == id)