diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-05-16 17:56:23 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-05-16 17:56:36 +0200 |
| commit | a741bd6b83d1e374c8218b5439e26522499cc4ae (patch) | |
| tree | 796ef8b8ae2186a082f37a2aa4732c9bba7d2bdf /src | |
| parent | 6536e9e069616b862ebb774c7bef1b886c069350 (diff) | |
Absolute paths
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/func.rs | 21 | ||||
| -rw-r--r-- | src/eval/mod.rs | 8 | ||||
| -rw-r--r-- | src/image.rs | 3 | ||||
| -rw-r--r-- | src/lib.rs | 44 | ||||
| -rw-r--r-- | src/library/graphics/image.rs | 12 | ||||
| -rw-r--r-- | src/main.rs | 19 | ||||
| -rw-r--r-- | src/source.rs | 3 |
7 files changed, 77 insertions, 33 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs index 4c5761ab..f15b0241 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use super::{Args, Eval, Flow, Scope, Scopes, Value}; use crate::diag::{StrResult, TypResult}; use crate::model::{Content, NodeId, StyleMap}; +use crate::source::SourceId; use crate::syntax::ast::Expr; use crate::util::EcoString; use crate::Context; @@ -174,6 +175,8 @@ pub trait Node: 'static { /// A user-defined closure. #[derive(Hash)] pub struct Closure { + /// The location where the closure was defined. + pub location: Option<SourceId>, /// The name of the closure. pub name: Option<EcoString>, /// Captured values from outer scopes. @@ -212,18 +215,28 @@ impl Closure { // Backup the old control flow state. let prev_flow = ctx.flow.take(); + let detached = ctx.route.is_empty(); + if detached { + ctx.route = self.location.into_iter().collect(); + } // Evaluate the body. - let mut value = self.body.eval(ctx, &mut scp)?; + let result = self.body.eval(ctx, &mut scp); + + // Restore the old control flow state. + let flow = std::mem::replace(&mut ctx.flow, prev_flow); + if detached { + ctx.route.clear(); + } // Handle control flow. - match std::mem::replace(&mut ctx.flow, prev_flow) { - Some(Flow::Return(_, Some(explicit))) => value = explicit, + match flow { + Some(Flow::Return(_, Some(explicit))) => return Ok(explicit), Some(Flow::Return(_, None)) => {} Some(flow) => return Err(flow.forbidden())?, None => {} } - Ok(value) + result } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index b35cf1ef..79060137 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -707,6 +707,7 @@ impl Eval for ClosureExpr { // Define the actual function. Ok(Value::Func(Func::from_closure(Closure { + location: ctx.route.last().copied(), name, captured, params, @@ -765,6 +766,7 @@ impl Eval for ShowExpr { let body = self.body(); let span = body.span(); let func = Func::from_closure(Closure { + location: ctx.route.last().copied(), name: None, captured, params, @@ -945,9 +947,11 @@ impl Eval for IncludeExpr { /// Process an import of a module relative to the current location. fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> { // Load the source file. - let full = ctx.complete_path(path); + let full = ctx.locate(&path).at(span)?; let id = ctx.sources.load(&full).map_err(|err| match err.kind() { - std::io::ErrorKind::NotFound => error!(span, "file not found"), + std::io::ErrorKind::NotFound => { + error!(span, "file not found (searched at {})", full.display()) + } _ => error!(span, "failed to load source file ({})", err), })?; diff --git a/src/image.rs b/src/image.rs index 24a0deec..87c093d3 100644 --- a/src/image.rs +++ b/src/image.rs @@ -48,7 +48,8 @@ impl ImageStore { } } - /// Load and decode an image file from a path. + /// 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> { let hash = self.loader.resolve(path)?; Ok(*match self.files.entry(hash) { @@ -57,7 +57,7 @@ use std::hash::Hash; use std::path::PathBuf; use std::sync::Arc; -use crate::diag::TypResult; +use crate::diag::{StrResult, TypResult}; use crate::eval::{Eval, Flow, Module, Scope, Scopes}; use crate::font::FontStore; use crate::frame::Frame; @@ -65,6 +65,7 @@ use crate::image::ImageStore; use crate::loading::Loader; use crate::model::StyleMap; use crate::source::{SourceId, SourceStore}; +use crate::util::PathExt; /// The core context which holds the loader, configuration and cached artifacts. pub struct Context { @@ -76,6 +77,8 @@ pub struct Context { pub fonts: FontStore, /// Stores decoded images. pub images: ImageStore, + /// The compilation root. + root: PathBuf, /// The standard library scope. std: Arc<Scope>, /// The default styles. @@ -172,51 +175,64 @@ impl Context { self.evaluate(id)?.content.layout(self) } - /// Resolve a user-entered path (relative to the current evaluation - /// location) to be relative to the compilation environment's root. - pub fn complete_path(&self, path: &str) -> PathBuf { + /// Resolve a user-entered path to be relative to the compilation + /// environment's root. + pub 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()); + } + if let Some(dir) = self.sources.get(id).path().parent() { - return dir.join(path); + return Ok(dir.join(path).normalize()); } } - path.into() + return Err("cannot access file system from here".into()); } } /// A builder for a [`Context`]. /// /// This struct is created by [`Context::builder`]. +#[derive(Default)] pub struct ContextBuilder { + root: PathBuf, std: Option<Arc<Scope>>, styles: Option<Arc<StyleMap>>, } impl ContextBuilder { + /// The compilation root, relative to which absolute paths are. + 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). - pub fn std(mut self, std: impl Into<Arc<Scope>>) -> Self { + 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. - pub fn styles(mut self, styles: impl Into<Arc<StyleMap>>) -> Self { + 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 { + 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, - std: self.std.unwrap_or_else(|| Arc::new(library::new())), - styles: self.styles.unwrap_or_default(), + 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(), cache: HashMap::new(), route: vec![], @@ -226,12 +242,6 @@ impl ContextBuilder { } } -impl Default for ContextBuilder { - fn default() -> Self { - Self { std: None, styles: None } - } -} - /// An entry in the query cache. struct CacheEntry { /// The query's results. diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs index ee854130..6fd465cb 100644 --- a/src/library/graphics/image.rs +++ b/src/library/graphics/image.rs @@ -12,11 +12,15 @@ impl ImageNode { pub const FIT: ImageFit = ImageFit::Cover; fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content> { - let path = args.expect::<Spanned<EcoString>>("path to image file")?; - let full = ctx.complete_path(&path.v); + let Spanned { v: path, span } = + args.expect::<Spanned<EcoString>>("path to image file")?; + + let full = ctx.locate(&path).at(span)?; let id = ctx.images.load(&full).map_err(|err| match err.kind() { - std::io::ErrorKind::NotFound => error!(path.span, "file not found"), - _ => error!(path.span, "failed to load image ({})", err), + std::io::ErrorKind::NotFound => { + error!(span, "file not found (searched at {})", full.display()) + } + _ => error!(span, "failed to load image ({})", err), })?; let width = args.named("width")?; diff --git a/src/main.rs b/src/main.rs index daeff033..59ad5a71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,9 +22,10 @@ USAGE: OPTIONS: -h, --help Print this help + --root <dir> Configure the root for absolute paths ARGS: - <input.typ> Path input Typst file + <input.typ> Path to input Typst file [output.pdf] Path to output PDF "; @@ -44,9 +45,17 @@ fn main() { fn try_main(args: Args) -> Result<(), String> { // Create a loader for fonts and files. let mut loader = FsLoader::new(); + let mut builder = Context::builder(); + if let Some(root) = &args.root { + builder.root(root); + } // Search for fonts in the project directory. if let Some(dir) = args.input.parent() { + if args.root.is_none() { + builder.root(dir); + } + if dir.as_os_str().is_empty() { // Just a filename, so directory is current directory. loader.search_path("."); @@ -60,7 +69,7 @@ fn try_main(args: Args) -> Result<(), String> { // Create the context which holds loaded source files, fonts, images and // cached artifacts. - let mut ctx = Context::new(loader.wrap()); + let mut ctx = builder.build(loader.wrap()); // Ensure that the source file is not overwritten. if is_same_file(&args.input, &args.output).unwrap_or(false) { @@ -94,6 +103,7 @@ fn try_main(args: Args) -> Result<(), String> { struct Args { input: PathBuf, output: PathBuf, + root: Option<PathBuf>, } /// Parse command line arguments. @@ -104,7 +114,8 @@ fn parse_args() -> Result<Args, String> { std::process::exit(0); } - let input = args.free_from_str::<PathBuf>().map_err(|_| "missing input file")?; + let root = args.opt_value_from_str("--root").map_err(|_| "malformed root")?; + let input: PathBuf = args.free_from_str().map_err(|_| "missing input file")?; let output = match args.opt_free_from_str().ok().flatten() { Some(output) => output, None => { @@ -118,7 +129,7 @@ fn parse_args() -> Result<Args, String> { Err("too many arguments")?; } - Ok(Args { input, output }) + Ok(Args { input, output, root }) } /// Print an application-level error (independent from a source file). diff --git a/src/source.rs b/src/source.rs index a7c95255..780e12a8 100644 --- a/src/source.rs +++ b/src/source.rs @@ -54,7 +54,8 @@ impl SourceStore { } } - /// Load a source file from a path using the `loader`. + /// Load a source file from a path relative to the compilation environment's + /// root. /// /// If there already exists a source file for this path, it is /// [replaced](SourceFile::replace). |
