mirror of https://github.com/stelzo/typst.git
177 lines
6.1 KiB
Rust
177 lines
6.1 KiB
Rust
//! The compiler for the _Typst_ markup language.
|
|
//!
|
|
//! # Steps
|
|
//! - **Parsing:**
|
|
//! The compiler first transforms a plain string into an iterator of [tokens].
|
|
//! This token stream is [parsed] into a [syntax tree]. The tree itself is
|
|
//! untyped, but the [AST] module provides a typed layer over it.
|
|
//! - **Evaluation:**
|
|
//! The next step is to [evaluate] the markup. This produces a [module],
|
|
//! consisting of a scope of values that were exported by the code and
|
|
//! [content], a hierarchical, styled representation of what was written in
|
|
//! the source file. The elements of the content tree are well structured and
|
|
//! order-independent and thus much better suited for further processing than
|
|
//! the raw markup.
|
|
//! - **Typesetting:**
|
|
//! Next, the content is [typeset] into a [document] containing one [frame]
|
|
//! per page with items at fixed positions.
|
|
//! - **Exporting:**
|
|
//! These frames can finally be exported into an output format (currently
|
|
//! supported are [PDF] and [raster images]).
|
|
//!
|
|
//! [tokens]: syntax::SyntaxKind
|
|
//! [parsed]: syntax::parse
|
|
//! [syntax tree]: syntax::SyntaxNode
|
|
//! [AST]: syntax::ast
|
|
//! [evaluate]: eval::eval
|
|
//! [module]: eval::Module
|
|
//! [content]: model::Content
|
|
//! [typeset]: model::typeset
|
|
//! [document]: doc::Document
|
|
//! [frame]: doc::Frame
|
|
//! [PDF]: export::pdf
|
|
//! [raster images]: export::render
|
|
|
|
#![recursion_limit = "1000"]
|
|
#![allow(clippy::comparison_chain)]
|
|
|
|
extern crate self as typst;
|
|
|
|
#[macro_use]
|
|
pub mod util;
|
|
#[macro_use]
|
|
pub mod eval;
|
|
pub mod diag;
|
|
pub mod doc;
|
|
pub mod font;
|
|
pub mod geom;
|
|
pub mod image;
|
|
pub mod model;
|
|
|
|
#[doc(inline)]
|
|
pub use typst_syntax as syntax;
|
|
|
|
use std::collections::HashSet;
|
|
use std::ops::Range;
|
|
|
|
use comemo::{Prehashed, Track, TrackedMut};
|
|
use ecow::EcoString;
|
|
|
|
use crate::diag::{FileResult, SourceResult};
|
|
use crate::doc::Document;
|
|
use crate::eval::{Bytes, Datetime, Library, Route, Tracer};
|
|
use crate::font::{Font, FontBook};
|
|
use crate::syntax::{FileId, PackageSpec, Source, Span};
|
|
|
|
/// Compile a source file into a fully layouted document.
|
|
///
|
|
/// - Returns `Ok(document)` if there were no fatal errors.
|
|
/// - Returns `Err(errors)` if there were fatal errors.
|
|
///
|
|
/// Requires a mutable reference to a tracer. Such a tracer can be created with
|
|
/// `Tracer::new()`. Independently of whether compilation succeeded, calling
|
|
/// `tracer.warnings()` after compilation will return all compiler warnings.
|
|
#[tracing::instrument(skip_all)]
|
|
pub fn compile(world: &dyn World, tracer: &mut Tracer) -> SourceResult<Document> {
|
|
let route = Route::default();
|
|
|
|
// Call `track` just once to keep comemo's ID stable.
|
|
let world = world.track();
|
|
let mut tracer = tracer.track_mut();
|
|
|
|
// Try to evaluate the source file into a module.
|
|
let module = eval::eval(
|
|
world,
|
|
route.track(),
|
|
TrackedMut::reborrow_mut(&mut tracer),
|
|
&world.main(),
|
|
);
|
|
|
|
// Try to typeset it.
|
|
let res = module.and_then(|module| model::typeset(world, tracer, &module.content()));
|
|
|
|
// Deduplicate errors.
|
|
res.map_err(|err| {
|
|
let mut unique = HashSet::new();
|
|
err.into_iter()
|
|
.filter(|diagnostic| {
|
|
let hash = util::hash128(&(&diagnostic.span, &diagnostic.message));
|
|
unique.insert(hash)
|
|
})
|
|
.collect()
|
|
})
|
|
}
|
|
|
|
/// The environment in which typesetting occurs.
|
|
///
|
|
/// All loading functions (`main`, `source`, `file`, `font`) should perform
|
|
/// internal caching so that they are relatively cheap on repeated invocations
|
|
/// with the same argument. [`Source`], [`Bytes`], and [`Font`] are
|
|
/// all reference-counted and thus cheap to clone.
|
|
///
|
|
/// The compiler doesn't do the caching itself because the world has much more
|
|
/// information on when something can change. For example, fonts typically don't
|
|
/// change and can thus even be cached across multiple compilations (for
|
|
/// long-running applications like `typst watch`). Source files on the other
|
|
/// hand can change and should thus be cleared after. Advanced clients like
|
|
/// language servers can also retain the source files and [edit](Source::edit)
|
|
/// them in-place to benefit from better incremental performance.
|
|
#[comemo::track]
|
|
pub trait World {
|
|
/// The standard library.
|
|
fn library(&self) -> &Prehashed<Library>;
|
|
|
|
/// Metadata about all known fonts.
|
|
fn book(&self) -> &Prehashed<FontBook>;
|
|
|
|
/// Access the main source file.
|
|
fn main(&self) -> Source;
|
|
|
|
/// Try to access the specified source file.
|
|
///
|
|
/// The returned `Source` file's [id](Source::id) does not have to match the
|
|
/// given `id`. Due to symlinks, two different file id's can point to the
|
|
/// same on-disk file. Implementors can deduplicate and return the same
|
|
/// `Source` if they want to, but do not have to.
|
|
fn source(&self, id: FileId) -> FileResult<Source>;
|
|
|
|
/// Try to access the specified file.
|
|
fn file(&self, id: FileId) -> FileResult<Bytes>;
|
|
|
|
/// Try to access the font with the given index in the font book.
|
|
fn font(&self, index: usize) -> Option<Font>;
|
|
|
|
/// Get the current date.
|
|
///
|
|
/// If no offset is specified, the local date should be chosen. Otherwise,
|
|
/// the UTC date should be chosen with the corresponding offset in hours.
|
|
///
|
|
/// If this function returns `None`, Typst's `datetime` function will
|
|
/// return an error.
|
|
fn today(&self, offset: Option<i64>) -> Option<Datetime>;
|
|
|
|
/// A list of all available packages and optionally descriptions for them.
|
|
///
|
|
/// This function is optional to implement. It enhances the user experience
|
|
/// by enabling autocompletion for packages. Details about packages from the
|
|
/// `@preview` namespace are available from
|
|
/// `https://packages.typst.org/preview/index.json`.
|
|
fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
|
|
&[]
|
|
}
|
|
}
|
|
|
|
/// Helper methods on [`World`] implementations.
|
|
pub trait WorldExt {
|
|
/// Get the byte range for a span.
|
|
///
|
|
/// Returns `None` if the `Span` does not point into any source file.
|
|
fn range(&self, span: Span) -> Option<Range<usize>>;
|
|
}
|
|
|
|
impl<T: World> WorldExt for T {
|
|
fn range(&self, span: Span) -> Option<Range<usize>> {
|
|
self.source(span.id()?).ok()?.range(span)
|
|
}
|
|
}
|