mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Change the signature of World::main
(#4531)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
4d8976b619
commit
a3f3a1a833
@ -5,16 +5,16 @@ use std::path::{Path, PathBuf};
|
||||
use chrono::{Datelike, Timelike};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use codespan_reporting::term;
|
||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
use ecow::{eco_format, EcoString};
|
||||
use parking_lot::RwLock;
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use typst::diag::{bail, FileError, Severity, SourceDiagnostic, StrResult, Warned};
|
||||
use typst::diag::{bail, Severity, SourceDiagnostic, StrResult, Warned};
|
||||
use typst::foundations::{Datetime, Smart};
|
||||
use typst::layout::{Frame, PageRanges};
|
||||
use typst::model::Document;
|
||||
use typst::syntax::{FileId, Source, Span};
|
||||
use typst::visualize::Color;
|
||||
use typst::{World, WorldExt};
|
||||
use typst::WorldExt;
|
||||
|
||||
use crate::args::{
|
||||
CompileCommand, DiagnosticFormat, Input, Output, OutputFormat, PageRangeArgument,
|
||||
@ -96,21 +96,6 @@ pub fn compile_once(
|
||||
Status::Compiling.print(command).unwrap();
|
||||
}
|
||||
|
||||
if let Err(errors) = world
|
||||
.source(world.main())
|
||||
.map_err(|err| hint_invalid_main_file(err, &command.common.input))
|
||||
{
|
||||
set_failed();
|
||||
if watching {
|
||||
Status::Error.print(command).unwrap();
|
||||
}
|
||||
|
||||
print_diagnostics(world, &errors, &[], command.common.diagnostic_format)
|
||||
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Warned { output, warnings } = typst::compile(world);
|
||||
|
||||
match output {
|
||||
@ -498,52 +483,6 @@ fn open_file(open: Option<&str>, path: &Path) -> StrResult<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds useful hints when the main source file couldn't be read
|
||||
/// and returns the final diagnostic.
|
||||
fn hint_invalid_main_file(
|
||||
file_error: FileError,
|
||||
input: &Input,
|
||||
) -> EcoVec<SourceDiagnostic> {
|
||||
let is_utf8_error = matches!(file_error, FileError::InvalidUtf8);
|
||||
let mut diagnostic =
|
||||
SourceDiagnostic::error(Span::detached(), EcoString::from(file_error));
|
||||
|
||||
// Attempt to provide helpful hints for UTF-8 errors.
|
||||
// Perhaps the user mistyped the filename.
|
||||
// For example, they could have written "file.pdf" instead of
|
||||
// "file.typ".
|
||||
if is_utf8_error {
|
||||
if let Input::Path(path) = input {
|
||||
let extension = path.extension();
|
||||
if extension.is_some_and(|extension| extension == "typ") {
|
||||
// No hints if the file is already a .typ file.
|
||||
// The file is indeed just invalid.
|
||||
return eco_vec![diagnostic];
|
||||
}
|
||||
|
||||
match extension {
|
||||
Some(extension) => {
|
||||
diagnostic.hint(eco_format!(
|
||||
"a file with the `.{}` extension is not usually a Typst file",
|
||||
extension.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
None => {
|
||||
diagnostic
|
||||
.hint("a file without an extension is not usually a Typst file");
|
||||
}
|
||||
};
|
||||
|
||||
if path.with_extension("typ").exists() {
|
||||
diagnostic.hint("check if you meant to use the `.typ` extension instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eco_vec![diagnostic]
|
||||
}
|
||||
|
||||
/// Print diagnostic messages to the terminal.
|
||||
pub fn print_diagnostics(
|
||||
world: &SystemWorld,
|
||||
|
@ -192,8 +192,8 @@ impl World for SystemWorld {
|
||||
&self.book
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
self.source(self.main).unwrap()
|
||||
fn main(&self) -> FileId {
|
||||
self.main
|
||||
}
|
||||
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
|
@ -139,8 +139,8 @@ mod tests {
|
||||
&self.base.book
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
self.main.clone()
|
||||
fn main(&self) -> FileId {
|
||||
self.main.id()
|
||||
}
|
||||
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
|
@ -91,6 +91,11 @@ impl FileId {
|
||||
Self::new(self.package().cloned(), self.vpath().join(path))
|
||||
}
|
||||
|
||||
/// The same file location, but with a different extension.
|
||||
pub fn with_extension(&self, extension: &str) -> Self {
|
||||
Self::new(self.package().cloned(), self.vpath().with_extension(extension))
|
||||
}
|
||||
|
||||
/// Construct from a raw number.
|
||||
pub(crate) const fn from_raw(v: u16) -> Self {
|
||||
Self(v)
|
||||
|
@ -85,6 +85,11 @@ impl VirtualPath {
|
||||
Self::new(path)
|
||||
}
|
||||
}
|
||||
|
||||
/// The same path, but with a different extension.
|
||||
pub fn with_extension(&self, extension: &str) -> Self {
|
||||
Self(self.0.with_extension(extension))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VirtualPath {
|
||||
|
@ -60,10 +60,12 @@ use std::collections::HashSet;
|
||||
use std::ops::{Deref, Range};
|
||||
|
||||
use comemo::{Track, Tracked, Validate};
|
||||
use ecow::{EcoString, EcoVec};
|
||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
use typst_timing::{timed, TimingScope};
|
||||
|
||||
use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult, Warned};
|
||||
use crate::diag::{
|
||||
warning, FileError, FileResult, SourceDiagnostic, SourceResult, Warned,
|
||||
};
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{
|
||||
Array, Bytes, Datetime, Dict, Module, Scope, StyleChain, Styles, Value,
|
||||
@ -108,13 +110,19 @@ fn compile_inner(
|
||||
let library = world.library();
|
||||
let styles = StyleChain::new(&library.styles);
|
||||
|
||||
// Fetch the main source file once.
|
||||
let main = world.main();
|
||||
let main = world
|
||||
.source(main)
|
||||
.map_err(|err| hint_invalid_main_file(world, err, main))?;
|
||||
|
||||
// First evaluate the main source file into a module.
|
||||
let content = crate::eval::eval(
|
||||
world,
|
||||
traced,
|
||||
sink.track_mut(),
|
||||
Route::default().track(),
|
||||
&world.main(),
|
||||
&main,
|
||||
)?
|
||||
.content();
|
||||
|
||||
@ -203,8 +211,8 @@ pub trait World: Send + Sync {
|
||||
/// Metadata about all known fonts.
|
||||
fn book(&self) -> &LazyHash<FontBook>;
|
||||
|
||||
/// Access the main source file.
|
||||
fn main(&self) -> Source;
|
||||
/// Get the file id of the main source file.
|
||||
fn main(&self) -> FileId;
|
||||
|
||||
/// Try to access the specified source file.
|
||||
fn source(&self, id: FileId) -> FileResult<Source>;
|
||||
@ -246,7 +254,7 @@ macro_rules! delegate_for_ptr {
|
||||
self.deref().book()
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
fn main(&self) -> FileId {
|
||||
self.deref().main()
|
||||
}
|
||||
|
||||
@ -402,3 +410,48 @@ fn prelude(global: &mut Scope) {
|
||||
global.define("horizon", Alignment::HORIZON);
|
||||
global.define("bottom", Alignment::BOTTOM);
|
||||
}
|
||||
|
||||
/// Adds useful hints when the main source file couldn't be read
|
||||
/// and returns the final diagnostic.
|
||||
fn hint_invalid_main_file(
|
||||
world: Tracked<dyn World + '_>,
|
||||
file_error: FileError,
|
||||
input: FileId,
|
||||
) -> EcoVec<SourceDiagnostic> {
|
||||
let is_utf8_error = matches!(file_error, FileError::InvalidUtf8);
|
||||
let mut diagnostic =
|
||||
SourceDiagnostic::error(Span::detached(), EcoString::from(file_error));
|
||||
|
||||
// Attempt to provide helpful hints for UTF-8 errors. Perhaps the user
|
||||
// mistyped the filename. For example, they could have written "file.pdf"
|
||||
// instead of "file.typ".
|
||||
if is_utf8_error {
|
||||
let path = input.vpath();
|
||||
let extension = path.as_rootless_path().extension();
|
||||
if extension.is_some_and(|extension| extension == "typ") {
|
||||
// No hints if the file is already a .typ file.
|
||||
// The file is indeed just invalid.
|
||||
return eco_vec![diagnostic];
|
||||
}
|
||||
|
||||
match extension {
|
||||
Some(extension) => {
|
||||
diagnostic.hint(eco_format!(
|
||||
"a file with the `.{}` extension is not usually a Typst file",
|
||||
extension.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
None => {
|
||||
diagnostic
|
||||
.hint("a file without an extension is not usually a Typst file");
|
||||
}
|
||||
};
|
||||
|
||||
if world.source(input.with_extension("typ")).is_ok() {
|
||||
diagnostic.hint("check if you meant to use the `.typ` extension instead");
|
||||
}
|
||||
}
|
||||
|
||||
eco_vec![diagnostic]
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use heck::{ToKebabCase, ToTitleCase};
|
||||
use pulldown_cmark as md;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typed_arena::Arena;
|
||||
use typst::diag::{FileResult, StrResult};
|
||||
use typst::diag::{FileError, FileResult, StrResult};
|
||||
use typst::foundations::{Bytes, Datetime};
|
||||
use typst::layout::{Abs, Point, Size};
|
||||
use typst::syntax::{FileId, Source, VirtualPath};
|
||||
@ -463,12 +463,16 @@ impl World for DocWorld {
|
||||
&FONTS.0
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
self.0.clone()
|
||||
fn main(&self) -> FileId {
|
||||
self.0.id()
|
||||
}
|
||||
|
||||
fn source(&self, _: FileId) -> FileResult<Source> {
|
||||
Ok(self.0.clone())
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
if id == self.0.id() {
|
||||
Ok(self.0.clone())
|
||||
} else {
|
||||
Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn file(&self, id: FileId) -> FileResult<Bytes> {
|
||||
|
@ -39,16 +39,20 @@ impl World for FuzzWorld {
|
||||
&self.book
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
self.source.clone()
|
||||
fn main(&self) -> FileId {
|
||||
self.source.id()
|
||||
}
|
||||
|
||||
fn source(&self, src: FileId) -> FileResult<Source> {
|
||||
Err(FileError::NotFound(src.vpath().as_rootless_path().into()))
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
if id == self.source.id() {
|
||||
Ok(self.source.clone())
|
||||
} else {
|
||||
Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn file(&self, src: FileId) -> FileResult<Bytes> {
|
||||
Err(FileError::NotFound(src.vpath().as_rootless_path().into()))
|
||||
fn file(&self, id: FileId) -> FileResult<Bytes> {
|
||||
Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
|
||||
}
|
||||
|
||||
fn font(&self, _: usize) -> Option<Font> {
|
||||
|
@ -43,8 +43,8 @@ impl World for TestWorld {
|
||||
&self.base.book
|
||||
}
|
||||
|
||||
fn main(&self) -> Source {
|
||||
self.main.clone()
|
||||
fn main(&self) -> FileId {
|
||||
self.main.id()
|
||||
}
|
||||
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user