Slim down context

This commit is contained in:
Laurenz 2022-05-25 11:16:03 +02:00
parent 362a7f2a8a
commit 3309ff9fe5
11 changed files with 122 additions and 124 deletions

View File

@ -1,4 +1,5 @@
use std::path::Path;
use std::sync::Arc;
use iai::{black_box, main, Iai};
use unscanny::Scanner;
@ -7,14 +8,14 @@ use typst::loading::MemLoader;
use typst::parse::{parse, TokenMode, Tokens};
use typst::source::SourceId;
use typst::syntax::highlight_node;
use typst::Context;
use typst::{Config, Context};
const SRC: &str = include_str!("bench.typ");
const FONT: &[u8] = include_bytes!("../fonts/IBMPlexSans-Regular.ttf");
fn context() -> (Context, SourceId) {
let loader = MemLoader::new().with(Path::new("font.ttf"), FONT).wrap();
let mut ctx = Context::new(loader);
let loader = MemLoader::new().with(Path::new("font.ttf"), FONT);
let mut ctx = Context::new(Arc::new(loader), Config::default());
let id = ctx.sources.provide(Path::new("src.typ"), SRC.to_string());
(ctx, id)
}

View File

@ -73,6 +73,32 @@ impl FontStore {
}
}
/// An ordered iterator over all font families this loader knows and details
/// about the faces that are part of them.
pub fn families(
&self,
) -> impl Iterator<Item = (&str, impl Iterator<Item = &FaceInfo>)> + '_ {
// Since the keys are lowercased, we instead use the family field of the
// first face's info.
let faces = self.loader.faces();
self.families.values().map(|ids| {
let family = faces[ids[0].0 as usize].family.as_str();
let infos = ids.iter().map(|&id| &faces[id.0 as usize]);
(family, infos)
})
}
/// Get a reference to a loaded face.
///
/// This panics if the face with this `id` was not loaded. This function
/// should only be called with ids returned by this store's
/// [`select()`](Self::select) and
/// [`select_fallback()`](Self::select_fallback) methods.
#[track_caller]
pub fn get(&self, id: FaceId) -> &Face {
self.faces[id.0 as usize].as_ref().expect("font face was not loaded")
}
/// Try to find and load a font face from the given `family` that matches
/// the given `variant` as closely as possible.
pub fn select(&mut self, family: &str, variant: FontVariant) -> Option<FaceId> {
@ -200,32 +226,6 @@ impl FontStore {
Some(id)
}
/// Get a reference to a loaded face.
///
/// This panics if the face with this `id` was not loaded. This function
/// should only be called with ids returned by this store's
/// [`select()`](Self::select) and
/// [`select_fallback()`](Self::select_fallback) methods.
#[track_caller]
pub fn get(&self, id: FaceId) -> &Face {
self.faces[id.0 as usize].as_ref().expect("font face was not loaded")
}
/// An ordered iterator over all font families this loader knows and details
/// about the faces that are part of them.
pub fn families(
&self,
) -> impl Iterator<Item = (&str, impl Iterator<Item = &FaceInfo>)> + '_ {
// Since the keys are lowercased, we instead use the family field of the
// first face's info.
let faces = self.loader.faces();
self.families.values().map(|ids| {
let family = faces[ids[0].0 as usize].family.as_str();
let infos = ids.iter().map(|&id| &faces[id.0 as usize]);
(family, infos)
})
}
}
/// How many words the two strings share in their prefix.

View File

@ -48,6 +48,16 @@ impl ImageStore {
}
}
/// Get a reference to a loaded image.
///
/// This panics if no image with this `id` was loaded. This function should
/// only be called with ids returned by this store's [`load()`](Self::load)
/// method.
#[track_caller]
pub fn get(&self, id: ImageId) -> &Image {
&self.images[id.0 as usize]
}
/// Load and decode an image file from a path relative to the compilation
/// environment's root.
pub fn load(&mut self, path: &Path) -> io::Result<ImageId> {
@ -64,16 +74,6 @@ impl ImageStore {
}
})
}
/// Get a reference to a loaded image.
///
/// This panics if no image with this `id` was loaded. This function should
/// only be called with ids returned by this store's [`load()`](Self::load)
/// method.
#[track_caller]
pub fn get(&self, id: ImageId) -> &Image {
&self.images[id.0 as usize]
}
}
/// A loaded image.

View File

@ -66,22 +66,16 @@ use crate::model::StyleMap;
use crate::source::{SourceId, SourceStore};
use crate::util::PathExt;
/// The core context which holds the loader, stores, and configuration.
/// The core context which holds the configuration and stores.
pub struct Context {
/// The loader the context was created with.
pub loader: Arc<dyn Loader>,
/// Stores loaded source files.
pub sources: SourceStore,
/// Stores parsed font faces.
pub fonts: FontStore,
/// Stores decoded images.
pub images: ImageStore,
/// The compilation root.
root: PathBuf,
/// The standard library scope.
std: Arc<Scope>,
/// The default styles.
styles: Arc<StyleMap>,
/// The context's configuration.
pub config: Config,
/// Cached modules.
modules: HashMap<SourceId, Module>,
/// The stack of imported files that led to evaluation of the current file.
@ -93,24 +87,18 @@ pub struct Context {
}
impl Context {
/// Create a new context with the default settings.
pub fn new(loader: Arc<dyn Loader>) -> Self {
Self::builder().build(loader)
}
/// Create a new context with advanced settings.
pub fn builder() -> ContextBuilder {
ContextBuilder::default()
}
/// A read-only reference to the standard library scope.
pub fn std(&self) -> &Scope {
&self.std
}
/// A read-only reference to the styles.
pub fn styles(&self) -> &StyleMap {
&self.styles
/// Create a new context.
pub fn new(loader: Arc<dyn Loader>, config: Config) -> Self {
Self {
sources: SourceStore::new(Arc::clone(&loader)),
fonts: FontStore::new(Arc::clone(&loader)),
images: ImageStore::new(loader),
config,
modules: HashMap::new(),
route: vec![],
deps: vec![],
flow: None,
}
}
/// Evaluate a source file and return the resulting module.
@ -144,7 +132,7 @@ impl Context {
self.route.push(id);
// Evaluate the module.
let std = self.std.clone();
let std = self.config.std.clone();
let mut scp = Scopes::new(Some(&std));
let result = ast.eval(self, &mut scp);
@ -175,10 +163,10 @@ impl Context {
/// Resolve a user-entered path to be relative to the compilation
/// environment's root.
pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
fn locate(&self, path: &str) -> StrResult<PathBuf> {
if let Some(&id) = self.route.last() {
if let Some(path) = path.strip_prefix('/') {
return Ok(self.root.join(path).normalize());
return Ok(self.config.root.join(path).normalize());
}
if let Some(dir) = self.sources.get(id).path().parent() {
@ -190,51 +178,70 @@ impl Context {
}
}
/// A builder for a [`Context`].
/// Compilation configuration.
pub struct Config {
/// The compilation root.
pub root: PathBuf,
/// The standard library scope.
pub std: Arc<Scope>,
/// The default styles.
pub styles: Arc<StyleMap>,
}
impl Config {
/// Create a new configuration builder.
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
impl Default for Config {
fn default() -> Self {
Self::builder().build()
}
}
/// A builder for a [`Config`].
///
/// This struct is created by [`Context::builder`].
#[derive(Default)]
pub struct ContextBuilder {
/// This struct is created by [`Config::builder`].
#[derive(Debug, Default, Clone)]
pub struct ConfigBuilder {
root: PathBuf,
std: Option<Arc<Scope>>,
styles: Option<Arc<StyleMap>>,
}
impl ContextBuilder {
impl ConfigBuilder {
/// The compilation root, relative to which absolute paths are.
///
/// Default: Empty path.
pub fn root(&mut self, root: impl Into<PathBuf>) -> &mut Self {
self.root = root.into();
self
}
/// The scope containing definitions that are available everywhere
/// (the standard library).
/// The scope containing definitions that are available everywhere.
///
/// Default: Typst's standard library.
pub fn std(&mut self, std: impl Into<Arc<Scope>>) -> &mut Self {
self.std = Some(std.into());
self
}
/// The default properties for page size, font selection and so on.
///
/// Default: Empty style map.
pub fn styles(&mut self, styles: impl Into<Arc<StyleMap>>) -> &mut Self {
self.styles = Some(styles.into());
self
}
/// Finish building the context by providing the `loader` used to load
/// fonts, images, source files and other resources.
pub fn build(&self, loader: Arc<dyn Loader>) -> Context {
Context {
sources: SourceStore::new(Arc::clone(&loader)),
fonts: FontStore::new(Arc::clone(&loader)),
images: ImageStore::new(Arc::clone(&loader)),
loader,
/// Finish building the configuration.
pub fn build(&self) -> Config {
Config {
root: self.root.clone(),
std: self.std.clone().unwrap_or_else(|| Arc::new(library::new())),
styles: self.styles.clone().unwrap_or_default(),
modules: HashMap::new(),
route: vec![],
deps: vec![],
flow: None,
}
}
}

View File

@ -42,7 +42,7 @@ pub fn eval(ctx: &mut Context, args: &mut Args) -> TypResult<Value> {
let prev_route = mem::take(&mut ctx.route);
// Evaluate the source.
let std = ctx.std.clone();
let std = ctx.config.std.clone();
let mut scp = Scopes::new(Some(&std));
let result = ast.eval(ctx, &mut scp);

View File

@ -1,7 +1,6 @@
use std::fs::{self, File};
use std::io;
use std::path::Path;
use std::sync::Arc;
use memmap2::Mmap;
use same_file::Handle;
@ -35,12 +34,6 @@ impl FsLoader {
self
}
/// Builder-style method to wrap the loader in an [`Arc`] to make it usable
/// with the [`Context`](crate::Context).
pub fn wrap(self) -> Arc<Self> {
Arc::new(self)
}
/// Search for fonts in the operating system's font directories.
pub fn search_system(&mut self) {
self.search_system_impl();

View File

@ -2,7 +2,6 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use super::{FileHash, Loader};
use crate::font::FaceInfo;
@ -31,12 +30,6 @@ impl MemLoader {
self
}
/// Builder-style method to wrap the loader in an [`Arc`] to make it usable
/// with the [`Context`](crate::Context).
pub fn wrap(self) -> Arc<Self> {
Arc::new(self)
}
/// Insert a path-file mapping. If the data forms a font, then that font
/// will be available for layouting.
///

View File

@ -2,9 +2,10 @@ use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process;
use std::sync::Arc;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term::{self, termcolor, Config, Styles};
use codespan_reporting::term::{self, termcolor};
use pico_args::Arguments;
use same_file::is_same_file;
use termcolor::{ColorChoice, StandardStream, WriteColor};
@ -17,7 +18,7 @@ use typst::loading::FsLoader;
use typst::parse::TokenMode;
use typst::source::SourceStore;
use typst::syntax;
use typst::Context;
use typst::{Config, Context};
/// What to do.
enum Command {
@ -172,7 +173,7 @@ fn print_help(help: &'static str) {
/// Print an application-level error (independent from a source file).
fn print_error(msg: &str) -> io::Result<()> {
let mut w = StandardStream::stderr(ColorChoice::Always);
let styles = Styles::default();
let styles = term::Styles::default();
w.set_color(&styles.header_error)?;
write!(w, "error")?;
@ -192,11 +193,11 @@ fn dispatch(command: Command) -> StrResult<()> {
/// Execute a typesetting command.
fn typeset(command: TypesetCommand) -> StrResult<()> {
let mut builder = Context::builder();
let mut config = Config::builder();
if let Some(root) = &command.root {
builder.root(root);
config.root(root);
} else if let Some(dir) = command.input.parent() {
builder.root(dir);
config.root(dir);
}
// Create a loader for fonts and files.
@ -204,7 +205,7 @@ fn typeset(command: TypesetCommand) -> StrResult<()> {
// Create the context which holds loaded source files, fonts, images and
// cached artifacts.
let mut ctx = builder.build(loader.wrap());
let mut ctx = Context::new(Arc::new(loader), config.build());
// Load the source file.
let id = ctx
@ -236,7 +237,7 @@ fn print_diagnostics(
errors: Vec<Error>,
) -> Result<(), codespan_reporting::files::Error> {
let mut w = StandardStream::stderr(ColorChoice::Always);
let config = Config { tab_width: 2, ..Default::default() };
let config = term::Config { tab_width: 2, ..Default::default() };
for error in errors {
// The main diagnostic.
@ -274,7 +275,7 @@ fn highlight(command: HighlightCommand) -> StrResult<()> {
/// Execute a font listing command.
fn fonts(command: FontsCommand) -> StrResult<()> {
let loader = FsLoader::new().with_system();
let fonts = FontStore::new(loader.wrap());
let fonts = FontStore::new(Arc::new(loader));
for (name, infos) in fonts.families() {
println!("{name}");

View File

@ -210,7 +210,7 @@ impl Content {
/// Layout this content into a collection of pages.
pub fn layout(&self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> {
let copy = ctx.styles.clone();
let copy = ctx.config.styles.clone();
let styles = StyleChain::with_root(&copy);
let scratch = Scratch::default();

View File

@ -59,6 +59,16 @@ impl SourceStore {
}
}
/// Get a reference to a loaded source file.
///
/// This panics if no source file with this `id` exists. This function
/// should only be called with ids returned by this store's
/// [`load()`](Self::load) and [`provide()`](Self::provide) methods.
#[track_caller]
pub fn get(&self, id: SourceId) -> &SourceFile {
&self.sources[id.0 as usize]
}
/// Load a source file from a path relative to the compilation environment's
/// root.
///
@ -109,14 +119,6 @@ impl SourceStore {
id
}
/// Get a reference to a loaded source file.
///
/// This panics if no source file with this `id` exists.
#[track_caller]
pub fn get(&self, id: SourceId) -> &SourceFile {
&self.sources[id.0 as usize]
}
/// Fully [replace](SourceFile::replace) the source text of a file.
///
/// This panics if no source file with this `id` exists.

View File

@ -19,7 +19,7 @@ use typst::loading::FsLoader;
use typst::model::StyleMap;
use typst::source::SourceFile;
use typst::syntax::Span;
use typst::{bail, Context};
use typst::{bail, Config, Context};
const TYP_DIR: &str = "./typ";
const REF_DIR: &str = "./ref";
@ -84,8 +84,9 @@ fn main() {
});
// Create loader and context.
let loader = FsLoader::new().with_path(FONT_DIR).wrap();
let mut ctx = Context::builder().std(std).styles(styles).build(loader);
let loader = FsLoader::new().with_path(FONT_DIR);
let config = Config::builder().std(std).styles(styles).build();
let mut ctx = Context::new(Arc::new(loader), config);
// Run all the tests.
let mut ok = 0;