summaryrefslogtreecommitdiff
path: root/crates/typst-cli
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-08-22 14:21:04 +0200
committerLaurenz <laurmaedje@gmail.com>2023-08-22 14:21:04 +0200
commitafc95ed19db8ded044d3b9b916fa0194cb882d0b (patch)
treeae9f5aa62398c1c81efe1f6d046fb2d631cb1c1c /crates/typst-cli
parent756bdb623c9deda1458506b1783a66d92f2d9414 (diff)
Virtual path type
Fixes #1937
Diffstat (limited to 'crates/typst-cli')
-rw-r--r--crates/typst-cli/Cargo.toml1
-rw-r--r--crates/typst-cli/src/compile.rs16
-rw-r--r--crates/typst-cli/src/world.rs43
3 files changed, 42 insertions, 18 deletions
diff --git a/crates/typst-cli/Cargo.toml b/crates/typst-cli/Cargo.toml
index 52439beb..c5b38be6 100644
--- a/crates/typst-cli/Cargo.toml
+++ b/crates/typst-cli/Cargo.toml
@@ -33,6 +33,7 @@ memmap2 = "0.5"
notify = "5"
once_cell = "1"
open = "4.0.2"
+pathdiff = "0.1"
same-file = "1"
serde = "1.0.184"
serde_json = "1"
diff --git a/crates/typst-cli/src/compile.rs b/crates/typst-cli/src/compile.rs
index 0fa66d62..5b51f422 100644
--- a/crates/typst-cli/src/compile.rs
+++ b/crates/typst-cli/src/compile.rs
@@ -231,11 +231,23 @@ pub fn print_diagnostics(
impl<'a> codespan_reporting::files::Files<'a> for SystemWorld {
type FileId = FileId;
- type Name = FileId;
+ type Name = String;
type Source = Source;
fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
- Ok(id)
+ let vpath = id.vpath();
+ Ok(if let Some(package) = id.package() {
+ format!("{package}{}", vpath.as_rooted_path().display())
+ } else {
+ // Try to express the path relative to the working directory.
+ vpath
+ .resolve(self.root())
+ .and_then(|abs| pathdiff::diff_paths(&abs, self.workdir()))
+ .as_deref()
+ .unwrap_or_else(|| vpath.as_rootless_path())
+ .to_string_lossy()
+ .into()
+ })
}
fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs
index fb8fc0c7..cfbe3791 100644
--- a/crates/typst-cli/src/world.rs
+++ b/crates/typst-cli/src/world.rs
@@ -11,8 +11,7 @@ use siphasher::sip128::{Hasher128, SipHasher13};
use typst::diag::{FileError, FileResult, StrResult};
use typst::eval::{eco_format, Bytes, Datetime, Library};
use typst::font::{Font, FontBook};
-use typst::syntax::{FileId, Source};
-use typst::util::PathExt;
+use typst::syntax::{FileId, Source, VirtualPath};
use typst::World;
use crate::args::SharedArgs;
@@ -21,6 +20,8 @@ use crate::package::prepare_package;
/// A world that provides access to the operating system.
pub struct SystemWorld {
+ /// The working directory.
+ workdir: Option<PathBuf>,
/// The root relative to which absolute paths are resolved.
root: PathBuf,
/// The input path.
@@ -49,7 +50,7 @@ impl SystemWorld {
searcher.search(&command.font_paths);
// Resolve the system-global input path.
- let system_input = command.input.canonicalize().map_err(|_| {
+ let input = command.input.canonicalize().map_err(|_| {
eco_format!("input file not found (searched at {})", command.input.display())
})?;
@@ -58,22 +59,21 @@ impl SystemWorld {
let path = command
.root
.as_deref()
- .or_else(|| system_input.parent())
+ .or_else(|| input.parent())
.unwrap_or(Path::new("."));
path.canonicalize().map_err(|_| {
eco_format!("root directory not found (searched at {})", path.display())
})?
};
- // Resolve the input path within the project.
- let project_input = system_input
- .strip_prefix(&root)
- .map(|path| Path::new("/").join(path))
- .map_err(|_| "input file must be contained in project root")?;
+ // Resolve the virtual path of the main file within the project root.
+ let main_path = VirtualPath::within_root(&input, &root)
+ .ok_or("input file must be contained in project root")?;
Ok(Self {
+ workdir: std::env::current_dir().ok(),
root,
- main: FileId::new(None, &project_input),
+ main: FileId::new(None, main_path),
library: Prehashed::new(typst_library::build()),
book: Prehashed::new(searcher.book),
fonts: searcher.fonts,
@@ -88,6 +88,16 @@ impl SystemWorld {
self.main
}
+ /// The root relative to which absolute paths are resolved.
+ pub fn root(&self) -> &Path {
+ &self.root
+ }
+
+ /// The current working directory.
+ pub fn workdir(&self) -> &Path {
+ self.workdir.as_deref().unwrap_or(Path::new("."))
+ }
+
/// Return all paths the last compilation depended on.
pub fn dependencies(&mut self) -> impl Iterator<Item = &Path> {
self.paths.get_mut().values().map(|slot| slot.system_path.as_path())
@@ -160,15 +170,16 @@ impl SystemWorld {
.or_insert_with(|| {
// Determine the root path relative to which the file path
// will be resolved.
- let root = match id.package() {
- Some(spec) => prepare_package(spec)?,
- None => self.root.clone(),
- };
+ let buf;
+ let mut root = &self.root;
+ if let Some(spec) = id.package() {
+ buf = prepare_package(spec)?;
+ root = &buf;
+ }
// Join the path to the root. If it tries to escape, deny
// access. Note: It can still escape via symlinks.
- system_path =
- root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?;
+ system_path = id.vpath().resolve(root).ok_or(FileError::AccessDenied)?;
PathHash::new(&system_path)
})