Slim down context
This commit is contained in:
parent
362a7f2a8a
commit
3309ff9fe5
@ -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)
|
||||
}
|
||||
|
52
src/font.rs
52
src/font.rs
@ -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.
|
||||
|
20
src/image.rs
20
src/image.rs
@ -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.
|
||||
|
105
src/lib.rs
105
src/lib.rs
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
///
|
||||
|
19
src/main.rs
19
src/main.rs
@ -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}");
|
||||
|
@ -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(©);
|
||||
let scratch = Scratch::default();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user