Reintroduce --root
This commit is contained in:
parent
7b92bd7c34
commit
04bffc4f12
@ -11,6 +11,10 @@ pub struct CliArguments {
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
|
||||
/// Configure the project root
|
||||
#[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")]
|
||||
pub root: Option<PathBuf>,
|
||||
|
||||
/// Add additional directories to search for fonts
|
||||
#[clap(
|
||||
long = "font-path",
|
||||
|
110
cli/src/main.rs
110
cli/src/main.rs
@ -93,6 +93,8 @@ fn typst_version() -> &'static str {
|
||||
|
||||
/// A summary of the input arguments relevant to compilation.
|
||||
struct CompileSettings {
|
||||
/// The project's root directory.
|
||||
root: Option<PathBuf>,
|
||||
/// The path to the input file.
|
||||
input: PathBuf,
|
||||
/// The path to the output file.
|
||||
@ -115,8 +117,9 @@ impl CompileSettings {
|
||||
fn new(
|
||||
input: PathBuf,
|
||||
output: Option<PathBuf>,
|
||||
watch: bool,
|
||||
root: Option<PathBuf>,
|
||||
font_paths: Vec<PathBuf>,
|
||||
watch: bool,
|
||||
open: Option<Option<String>>,
|
||||
ppi: Option<f32>,
|
||||
diagnostic_format: DiagnosticFormat,
|
||||
@ -126,6 +129,7 @@ impl CompileSettings {
|
||||
None => input.with_extension("pdf"),
|
||||
};
|
||||
Self {
|
||||
root,
|
||||
input,
|
||||
output,
|
||||
watch,
|
||||
@ -149,7 +153,16 @@ impl CompileSettings {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Self::new(input, output, watch, args.font_paths, open, ppi, diagnostic_format)
|
||||
Self::new(
|
||||
input,
|
||||
output,
|
||||
args.root,
|
||||
args.font_paths,
|
||||
watch,
|
||||
open,
|
||||
ppi,
|
||||
diagnostic_format,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,22 +192,22 @@ impl FontsSettings {
|
||||
}
|
||||
|
||||
/// Execute a compilation command.
|
||||
fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
fn compile(mut settings: CompileSettings) -> StrResult<()> {
|
||||
// Create the world that serves sources, files, and fonts.
|
||||
let mut world = SystemWorld::new(&command.input, &command.font_paths);
|
||||
let mut world = SystemWorld::new(&settings)?;
|
||||
|
||||
// Perform initial compilation.
|
||||
let ok = compile_once(&mut world, &command)?;
|
||||
let ok = compile_once(&mut world, &settings)?;
|
||||
|
||||
// Open the file if requested, this must be done on the first **successful**
|
||||
// compilation.
|
||||
if ok {
|
||||
if let Some(open) = command.open.take() {
|
||||
open_file(open.as_deref(), &command.output)?;
|
||||
if let Some(open) = settings.open.take() {
|
||||
open_file(open.as_deref(), &settings.output)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !command.watch {
|
||||
if !settings.watch {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -219,7 +232,7 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
if event
|
||||
.paths
|
||||
.iter()
|
||||
.all(|path| is_same_file(path, &command.output).unwrap_or(false))
|
||||
.all(|path| is_same_file(path, &settings.output).unwrap_or(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -232,7 +245,7 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
let dependencies = world.dependencies();
|
||||
|
||||
// Recompile.
|
||||
let ok = compile_once(&mut world, &command)?;
|
||||
let ok = compile_once(&mut world, &settings)?;
|
||||
comemo::evict(10);
|
||||
|
||||
// Adjust the watching.
|
||||
@ -241,8 +254,8 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
// Open the file if requested, this must be done on the first
|
||||
// **successful** compilation
|
||||
if ok {
|
||||
if let Some(open) = command.open.take() {
|
||||
open_file(open.as_deref(), &command.output)?;
|
||||
if let Some(open) = settings.open.take() {
|
||||
open_file(open.as_deref(), &settings.output)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,11 +266,11 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
///
|
||||
/// Returns whether it compiled without errors.
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
|
||||
fn compile_once(world: &mut SystemWorld, settings: &CompileSettings) -> StrResult<bool> {
|
||||
tracing::info!("Starting compilation");
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
status(command, Status::Compiling).unwrap();
|
||||
status(settings, Status::Compiling).unwrap();
|
||||
|
||||
// Reset everything and ensure that the main file is still present.
|
||||
world.reset();
|
||||
@ -269,8 +282,8 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
||||
match result {
|
||||
// Export the PDF / PNG.
|
||||
Ok(document) => {
|
||||
export(&document, command)?;
|
||||
status(command, Status::Success(duration)).unwrap();
|
||||
export(&document, settings)?;
|
||||
status(settings, Status::Success(duration)).unwrap();
|
||||
tracing::info!("Compilation succeeded in {duration:?}");
|
||||
Ok(true)
|
||||
}
|
||||
@ -278,8 +291,8 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
||||
// Print diagnostics.
|
||||
Err(errors) => {
|
||||
set_failed();
|
||||
status(command, Status::Error).unwrap();
|
||||
print_diagnostics(world, *errors, command.diagnostic_format)
|
||||
status(settings, Status::Error).unwrap();
|
||||
print_diagnostics(world, *errors, settings.diagnostic_format)
|
||||
.map_err(|_| "failed to print diagnostics")?;
|
||||
tracing::info!("Compilation failed after {duration:?}");
|
||||
Ok(false)
|
||||
@ -288,11 +301,11 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
||||
}
|
||||
|
||||
/// Export into the target format.
|
||||
fn export(document: &Document, command: &CompileSettings) -> StrResult<()> {
|
||||
match command.output.extension() {
|
||||
fn export(document: &Document, settings: &CompileSettings) -> StrResult<()> {
|
||||
match settings.output.extension() {
|
||||
Some(ext) if ext.eq_ignore_ascii_case("png") => {
|
||||
// Determine whether we have a `{n}` numbering.
|
||||
let string = command.output.to_str().unwrap_or_default();
|
||||
let string = settings.output.to_str().unwrap_or_default();
|
||||
let numbered = string.contains("{n}");
|
||||
if !numbered && document.pages.len() > 1 {
|
||||
bail!("cannot export multiple PNGs without `{{n}}` in output path");
|
||||
@ -302,7 +315,7 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> {
|
||||
// first page should be numbered "001" if there are between 100 and
|
||||
// 999 pages.
|
||||
let width = 1 + document.pages.len().checked_ilog10().unwrap_or(0) as usize;
|
||||
let ppi = command.ppi.unwrap_or(2.0);
|
||||
let ppi = settings.ppi.unwrap_or(2.0);
|
||||
let mut storage;
|
||||
|
||||
for (i, frame) in document.pages.iter().enumerate() {
|
||||
@ -311,14 +324,15 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> {
|
||||
storage = string.replace("{n}", &format!("{:0width$}", i + 1));
|
||||
Path::new(&storage)
|
||||
} else {
|
||||
command.output.as_path()
|
||||
settings.output.as_path()
|
||||
};
|
||||
pixmap.save_png(path).map_err(|_| "failed to write PNG file")?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let buffer = typst::export::pdf(document);
|
||||
fs::write(&command.output, buffer).map_err(|_| "failed to write PDF file")?;
|
||||
fs::write(&settings.output, buffer)
|
||||
.map_err(|_| "failed to write PDF file")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -326,14 +340,14 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> {
|
||||
|
||||
/// Clear the terminal and render the status message.
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn status(command: &CompileSettings, status: Status) -> io::Result<()> {
|
||||
if !command.watch {
|
||||
fn status(settings: &CompileSettings, status: Status) -> io::Result<()> {
|
||||
if !settings.watch {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let esc = 27 as char;
|
||||
let input = command.input.display();
|
||||
let output = command.output.display();
|
||||
let input = settings.input.display();
|
||||
let output = settings.output.display();
|
||||
let time = chrono::offset::Local::now();
|
||||
let timestamp = time.format("%H:%M:%S");
|
||||
let message = status.message();
|
||||
@ -506,31 +520,43 @@ struct PathSlot {
|
||||
}
|
||||
|
||||
impl SystemWorld {
|
||||
fn new(input: &Path, font_paths: &[PathBuf]) -> Self {
|
||||
fn new(settings: &CompileSettings) -> StrResult<Self> {
|
||||
let mut searcher = FontSearcher::new();
|
||||
searcher.search(font_paths);
|
||||
searcher.search(&settings.font_paths);
|
||||
|
||||
let root = input
|
||||
.canonicalize()
|
||||
.ok()
|
||||
.as_ref()
|
||||
.and_then(|path| path.parent())
|
||||
.unwrap_or(Path::new("."))
|
||||
.to_owned();
|
||||
// Resolve the system-global input path.
|
||||
let system_input = settings.input.canonicalize().map_err(|_| {
|
||||
eco_format!("input file not found (searched at {})", settings.input.display())
|
||||
})?;
|
||||
|
||||
let file = input.file_name().unwrap_or(input.as_os_str());
|
||||
let main = FileId::new(None, Path::new(file));
|
||||
// Resolve the system-global root directory.
|
||||
let root = {
|
||||
let path = settings
|
||||
.root
|
||||
.as_deref()
|
||||
.or_else(|| system_input.parent())
|
||||
.unwrap_or(Path::new("."));
|
||||
path.canonicalize().map_err(|_| {
|
||||
eco_format!("root directory not found (searched at {})", path.display())
|
||||
})?
|
||||
};
|
||||
|
||||
Self {
|
||||
// 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")?;
|
||||
|
||||
Ok(Self {
|
||||
root,
|
||||
main,
|
||||
main: FileId::new(None, &project_input),
|
||||
library: Prehashed::new(typst_library::build()),
|
||||
book: Prehashed::new(searcher.book),
|
||||
fonts: searcher.fonts,
|
||||
hashes: RefCell::default(),
|
||||
paths: RefCell::default(),
|
||||
today: OnceCell::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
|
||||
return Html::new(format!("<pre>{}</pre>", highlighted.as_str()));
|
||||
}
|
||||
|
||||
let id = FileId::new(None, Path::new("main.typ"));
|
||||
let id = FileId::new(None, Path::new("/main.typ"));
|
||||
let source = Source::new(id, compile);
|
||||
let world = DocWorld(source);
|
||||
let mut frames = match typst::compile(&world) {
|
||||
@ -486,7 +486,7 @@ impl World for DocWorld {
|
||||
fn file(&self, id: FileId) -> FileResult<Bytes> {
|
||||
assert!(id.package().is_none());
|
||||
Ok(FILES
|
||||
.get_file(id.path())
|
||||
.get_file(id.path().strip_prefix("/").unwrap())
|
||||
.unwrap_or_else(|| panic!("failed to load {:?}", id.path().display()))
|
||||
.contents()
|
||||
.into())
|
||||
|
@ -1767,8 +1767,7 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
|
||||
manifest.validate(&spec).at(span)?;
|
||||
|
||||
// Evaluate the entry point.
|
||||
let entrypoint = Path::new("/").join(manifest.package.entrypoint.as_str());
|
||||
let entrypoint_id = FileId::new(Some(spec), &entrypoint);
|
||||
let entrypoint_id = manifest_id.join(&manifest.package.entrypoint).at(span)?;
|
||||
let source = vm.world().source(entrypoint_id).at(span)?;
|
||||
let point = || Tracepoint::Import;
|
||||
Ok(eval(vm.world(), vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source)
|
||||
|
50
src/file.rs
50
src/file.rs
@ -29,28 +29,44 @@ type Pair = &'static (Option<PackageSpec>, PathBuf);
|
||||
|
||||
/// Identifies a file.
|
||||
///
|
||||
/// This type is interned and thus cheap to clone, compare, and hash.
|
||||
/// This type is globally interned and thus cheap to copy, compare, and hash.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct FileId(u16);
|
||||
|
||||
impl FileId {
|
||||
/// Create a new interned file specification.
|
||||
///
|
||||
/// Normalizes the path before interning.
|
||||
/// The path must start with a `/` or this function will panic.
|
||||
/// Note that the path is normalized before interning.
|
||||
#[track_caller]
|
||||
pub fn new(package: Option<PackageSpec>, path: &Path) -> Self {
|
||||
assert_eq!(
|
||||
path.components().next(),
|
||||
Some(std::path::Component::RootDir),
|
||||
"file path must be absolute within project or package: {}",
|
||||
path.display(),
|
||||
);
|
||||
|
||||
// Try to find an existing entry that we can reuse.
|
||||
let pair = (package, path.normalize());
|
||||
if let Some(&id) = INTERNER.read().unwrap().to_id.get(&pair) {
|
||||
return id;
|
||||
}
|
||||
|
||||
let mut interner = INTERNER.write().unwrap();
|
||||
interner.to_id.get(&pair).copied().unwrap_or_else(|| {
|
||||
let leaked = Box::leak(Box::new(pair));
|
||||
let len = interner.from_id.len();
|
||||
if len >= usize::from(u16::MAX) {
|
||||
panic!("too many file specifications");
|
||||
}
|
||||
let id = FileId(len as u16);
|
||||
interner.to_id.insert(leaked, id);
|
||||
interner.from_id.push(leaked);
|
||||
id
|
||||
})
|
||||
let len = interner.from_id.len();
|
||||
if len >= usize::from(u16::MAX) {
|
||||
panic!("too many file specifications");
|
||||
}
|
||||
|
||||
// Create a new entry forever by leaking the pair. We can't leak more
|
||||
// than 2^16 pair (and typically will leak a lot less), so its not a
|
||||
// big deal.
|
||||
let id = FileId(len as u16);
|
||||
let leaked = Box::leak(Box::new(pair));
|
||||
interner.to_id.insert(leaked, id);
|
||||
interner.from_id.push(leaked);
|
||||
id
|
||||
}
|
||||
|
||||
/// Get an id that does not identify any real file.
|
||||
@ -72,11 +88,11 @@ impl FileId {
|
||||
}
|
||||
}
|
||||
|
||||
/// The normalized path to the file (within the package if there's a
|
||||
/// package).
|
||||
/// The absolute and normalized path to the file _within_ the project or
|
||||
/// package.
|
||||
pub fn path(&self) -> &'static Path {
|
||||
if self.is_detached() {
|
||||
Path::new("<detached>")
|
||||
Path::new("/detached.typ")
|
||||
} else {
|
||||
&self.pair().1
|
||||
}
|
||||
@ -117,7 +133,7 @@ impl Display for FileId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let path = self.path().display();
|
||||
match self.package() {
|
||||
Some(package) => write!(f, "{package}/{path}"),
|
||||
Some(package) => write!(f, "{package}{path}"),
|
||||
None => write!(f, "{path}"),
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,6 @@ impl PathExt for Path {
|
||||
match component {
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => match out.components().next_back() {
|
||||
Some(Component::RootDir) => {}
|
||||
Some(Component::Normal(_)) => {
|
||||
out.pop();
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ const REF_DIR: &str = "ref";
|
||||
const PNG_DIR: &str = "png";
|
||||
const PDF_DIR: &str = "pdf";
|
||||
const FONT_DIR: &str = "../assets/fonts";
|
||||
const FILE_DIR: &str = "../assets/files";
|
||||
const ASSET_DIR: &str = "../assets";
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[clap(name = "typst-test", author)]
|
||||
@ -281,7 +281,7 @@ impl World for TestWorld {
|
||||
|
||||
impl TestWorld {
|
||||
fn set(&mut self, path: &Path, text: String) -> Source {
|
||||
self.main = FileId::new(None, path);
|
||||
self.main = FileId::new(None, &Path::new("/").join(path));
|
||||
let mut slot = self.slot(self.main).unwrap();
|
||||
let source = Source::new(self.main, text);
|
||||
slot.source = OnceCell::from(Ok(source.clone()));
|
||||
@ -289,11 +289,9 @@ impl TestWorld {
|
||||
}
|
||||
|
||||
fn slot(&self, id: FileId) -> FileResult<RefMut<PathSlot>> {
|
||||
let path = id.path();
|
||||
let root: PathBuf = match id.package() {
|
||||
Some(spec) => format!("packages/{}-{}", spec.name, spec.version).into(),
|
||||
None if path.is_relative() => PathBuf::new(),
|
||||
None => FILE_DIR.into(),
|
||||
None => PathBuf::new(),
|
||||
};
|
||||
|
||||
let system_path = root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?;
|
||||
@ -310,16 +308,18 @@ impl TestWorld {
|
||||
|
||||
/// Read as file.
|
||||
fn read(path: &Path) -> FileResult<Vec<u8>> {
|
||||
let suffix = path
|
||||
.strip_prefix(FILE_DIR)
|
||||
.map(|suffix| Path::new("/").join(suffix))
|
||||
.unwrap_or_else(|_| path.into());
|
||||
// Basically symlinks `assets/files` to `tests/files` so that the assets
|
||||
// are within the test project root.
|
||||
let mut resolved = path.to_path_buf();
|
||||
if path.starts_with("files/") {
|
||||
resolved = Path::new(ASSET_DIR).join(path);
|
||||
}
|
||||
|
||||
let f = |e| FileError::from_io(e, &suffix);
|
||||
if fs::metadata(path).map_err(f)?.is_dir() {
|
||||
let f = |e| FileError::from_io(e, path);
|
||||
if fs::metadata(&resolved).map_err(f)?.is_dir() {
|
||||
Err(FileError::IsDirectory)
|
||||
} else {
|
||||
fs::read(path).map_err(f)
|
||||
fs::read(&resolved).map_err(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,4 +20,4 @@
|
||||
#locate(loc => [Citation @distress on page #loc.page()])
|
||||
|
||||
#pagebreak()
|
||||
#bibliography("/works.bib", style: "chicago-notes")
|
||||
#bibliography("/files/works.bib", style: "chicago-notes")
|
||||
|
@ -7,7 +7,7 @@ A pirate. @arrgh \
|
||||
#set text(2pt)
|
||||
#hide[
|
||||
A @arrgh pirate.
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -21,4 +21,4 @@ Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel
|
||||
zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges
|
||||
von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel.
|
||||
|
||||
#align(center, image("/graph.png", width: 75%))
|
||||
#align(center, image("/files/graph.png", width: 75%))
|
||||
|
@ -112,8 +112,8 @@
|
||||
|
||||
---
|
||||
// Some non-text stuff.
|
||||
// Error: 9-21 file is not valid utf-8
|
||||
#import "/rhino.png"
|
||||
// Error: 9-27 file is not valid utf-8
|
||||
#import "/files/rhino.png"
|
||||
|
||||
---
|
||||
// Unresolved import.
|
||||
|
@ -40,7 +40,7 @@ Hello *#x*
|
||||
// Test relative path resolving in layout phase.
|
||||
#let choice = ("monkey.svg", "rhino.png", "tiger.jpg")
|
||||
#set enum(numbering: n => {
|
||||
let path = "/" + choice.at(n - 1)
|
||||
let path = "/files/" + choice.at(n - 1)
|
||||
move(dy: -0.15em, image(path, width: 1em, height: 1em))
|
||||
})
|
||||
|
||||
|
@ -54,6 +54,6 @@ World
|
||||
---
|
||||
// Test absolute path in layout phase.
|
||||
|
||||
#show "GRAPH": image("/graph.png")
|
||||
#show "GRAPH": image("/files/graph.png")
|
||||
|
||||
The GRAPH has nodes.
|
||||
|
@ -3,22 +3,22 @@
|
||||
|
||||
---
|
||||
// Test reading plain text files
|
||||
#let data = read("/hello.txt")
|
||||
#let data = read("/files/hello.txt")
|
||||
#test(data, "Hello, world!")
|
||||
|
||||
---
|
||||
// Error: 18-32 file not found (searched at /missing.txt)
|
||||
#let data = read("/missing.txt")
|
||||
// Error: 18-38 file not found (searched at files/missing.txt)
|
||||
#let data = read("/files/missing.txt")
|
||||
|
||||
---
|
||||
// Error: 18-28 file is not valid utf-8
|
||||
#let data = read("/bad.txt")
|
||||
// Error: 18-34 file is not valid utf-8
|
||||
#let data = read("/files/bad.txt")
|
||||
|
||||
---
|
||||
// Test reading CSV data.
|
||||
// Ref: true
|
||||
#set page(width: auto)
|
||||
#let data = csv("/zoo.csv")
|
||||
#let data = csv("/files/zoo.csv")
|
||||
#let cells = data.at(0).map(strong) + data.slice(1).flatten()
|
||||
#table(columns: data.at(0).len(), ..cells)
|
||||
|
||||
@ -27,23 +27,23 @@
|
||||
#csv("nope.csv")
|
||||
|
||||
---
|
||||
// Error: 6-16 failed to parse csv file: found 3 instead of 2 fields in line 3
|
||||
#csv("/bad.csv")
|
||||
// Error: 6-22 failed to parse csv file: found 3 instead of 2 fields in line 3
|
||||
#csv("/files/bad.csv")
|
||||
|
||||
---
|
||||
// Test reading JSON data.
|
||||
#let data = json("/zoo.json")
|
||||
#let data = json("/files/zoo.json")
|
||||
#test(data.len(), 3)
|
||||
#test(data.at(0).name, "Debby")
|
||||
#test(data.at(2).weight, 150)
|
||||
|
||||
---
|
||||
// Error: 7-18 failed to parse json file: syntax error in line 3
|
||||
#json("/bad.json")
|
||||
// Error: 7-24 failed to parse json file: syntax error in line 3
|
||||
#json("/files/bad.json")
|
||||
|
||||
---
|
||||
// Test reading TOML data.
|
||||
#let data = toml("/toml-types.toml")
|
||||
#let data = toml("/files/toml-types.toml")
|
||||
#test(data.string, "wonderful")
|
||||
#test(data.integer, 42)
|
||||
#test(data.float, 3.14)
|
||||
@ -62,12 +62,12 @@
|
||||
))
|
||||
|
||||
---
|
||||
// Error: 7-18 failed to parse toml file: expected `.`, `=`, index 15-15
|
||||
#toml("/bad.toml")
|
||||
// Error: 7-24 failed to parse toml file: expected `.`, `=`, index 15-15
|
||||
#toml("/files/bad.toml")
|
||||
|
||||
---
|
||||
// Test reading YAML data
|
||||
#let data = yaml("/yaml-types.yaml")
|
||||
#let data = yaml("/files/yaml-types.yaml")
|
||||
#test(data.len(), 7)
|
||||
#test(data.null_key, (none, none))
|
||||
#test(data.string, "text")
|
||||
@ -80,12 +80,12 @@
|
||||
---
|
||||
|
||||
---
|
||||
// Error: 7-18 failed to parse yaml file: while parsing a flow sequence, expected ',' or ']' at line 2 column 1
|
||||
#yaml("/bad.yaml")
|
||||
// Error: 7-24 failed to parse yaml file: while parsing a flow sequence, expected ',' or ']' at line 2 column 1
|
||||
#yaml("/files/bad.yaml")
|
||||
|
||||
---
|
||||
// Test reading XML data.
|
||||
#let data = xml("/data.xml")
|
||||
#let data = xml("/files/data.xml")
|
||||
#test(data, ((
|
||||
tag: "data",
|
||||
attrs: (:),
|
||||
@ -109,5 +109,5 @@
|
||||
),))
|
||||
|
||||
---
|
||||
// Error: 6-16 failed to parse xml file: found closing tag 'data' instead of 'hello' in line 3
|
||||
#xml("/bad.xml")
|
||||
// Error: 6-22 failed to parse xml file: found closing tag 'data' instead of 'hello' in line 3
|
||||
#xml("/files/bad.xml")
|
||||
|
@ -23,7 +23,7 @@
|
||||
columns: 4 * (1fr,),
|
||||
row-gutter: 10pt,
|
||||
column-gutter: (0pt, 10%),
|
||||
align(top, image("/rhino.png")),
|
||||
align(top, image("/files/rhino.png")),
|
||||
align(top, rect(inset: 0pt, fill: eastern, align(right)[LoL])),
|
||||
[rofl],
|
||||
[\ A] * 3,
|
||||
|
@ -22,7 +22,7 @@ Hi #box(pad(left: 10pt)[A]) there
|
||||
// Test that the pad element doesn't consume the whole region.
|
||||
#set page(height: 6cm)
|
||||
#align(left)[Before]
|
||||
#pad(10pt, image("/tiger.jpg"))
|
||||
#pad(10pt, image("/files/tiger.jpg"))
|
||||
#align(right)[After]
|
||||
|
||||
---
|
||||
|
@ -41,7 +41,7 @@ Lריווח #h(1cm) R
|
||||
---
|
||||
// Test inline object.
|
||||
#set text(lang: "he")
|
||||
קרנפיםRh#box(image("/rhino.png", height: 11pt))inoחיים
|
||||
קרנפיםRh#box(image("/files/rhino.png", height: 11pt))inoחיים
|
||||
|
||||
---
|
||||
// Test whether L1 whitespace resetting destroys stuff.
|
||||
|
@ -9,10 +9,10 @@ The first paragraph has no indent.
|
||||
|
||||
But the second one does.
|
||||
|
||||
#box(image("/tiger.jpg", height: 6pt))
|
||||
#box(image("/files/tiger.jpg", height: 6pt))
|
||||
starts a paragraph, also with indent.
|
||||
|
||||
#align(center, image("/rhino.png", width: 1cm))
|
||||
#align(center, image("/files/rhino.png", width: 1cm))
|
||||
|
||||
= Headings
|
||||
- And lists.
|
||||
|
@ -7,7 +7,7 @@
|
||||
dx: -10pt,
|
||||
dy: -10pt,
|
||||
image(
|
||||
"/tiger.jpg",
|
||||
"/files/tiger.jpg",
|
||||
fit: "cover",
|
||||
width: 100% + 20pt,
|
||||
height: 100% + 20pt,
|
||||
|
@ -5,7 +5,7 @@
|
||||
#place(bottom + center)[© Typst]
|
||||
|
||||
= Placement
|
||||
#place(right, image("/tiger.jpg", width: 1.8cm))
|
||||
#place(right, image("/files/tiger.jpg", width: 1.8cm))
|
||||
Hi there. This is \
|
||||
a placed element. \
|
||||
Unfortunately, \
|
||||
|
@ -31,13 +31,13 @@ nor #xetex!
|
||||
// Test combination of scaling and rotation.
|
||||
#set page(height: 80pt)
|
||||
#align(center + horizon,
|
||||
rotate(20deg, scale(70%, image("/tiger.jpg")))
|
||||
rotate(20deg, scale(70%, image("/files/tiger.jpg")))
|
||||
)
|
||||
|
||||
---
|
||||
// Test setting rotation origin.
|
||||
#rotate(10deg, origin: top + left,
|
||||
image("/tiger.jpg", width: 50%)
|
||||
image("/files/tiger.jpg", width: 50%)
|
||||
)
|
||||
|
||||
---
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
// Test images and font fallback.
|
||||
#let monkey = move(dy: 0.2em, image("/monkey.svg", height: 1em))
|
||||
#let monkey = move(dy: 0.2em, image("/files/monkey.svg", height: 1em))
|
||||
$ sum_(i=#emoji.apple)^#emoji.apple.red i + monkey/2 $
|
||||
|
||||
---
|
||||
|
@ -4,4 +4,4 @@
|
||||
@netwok, @issue201, @arrgh, @quark, @distress,
|
||||
@glacier-melt, @issue201, @tolkien54, @sharing, @restful
|
||||
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
|
@ -5,19 +5,24 @@
|
||||
= Introduction <arrgh>
|
||||
// Error: 1-7 label occurs in the document and its bibliography
|
||||
@arrgh
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
|
||||
---
|
||||
#set page(width: 200pt)
|
||||
= Details
|
||||
See also #cite("arrgh", "distress", [p. 22]), @arrgh[p. 4], and @distress[p. 5].
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
|
||||
---
|
||||
// Test unconventional order.
|
||||
#set page(width: 200pt)
|
||||
#bibliography("/works.bib", title: [Works to be cited], style: "chicago-author-date")
|
||||
#bibliography(
|
||||
"/files/works.bib",
|
||||
title: [Works to be cited],
|
||||
style: "chicago-author-date",
|
||||
)
|
||||
#line(length: 100%)
|
||||
|
||||
#[#set cite(brackets: false)
|
||||
As described by @netwok],
|
||||
the net-work is a creature of its own.
|
||||
@ -25,8 +30,8 @@ This is close to piratery! @arrgh
|
||||
And quark! @quark
|
||||
|
||||
---
|
||||
// Error: 15-43 duplicate bibliography keys: arrgh, distress, glacier-melt, issue201, mcintosh_anxiety, netwok, psychology25, quark, restful, sharing, tolkien54
|
||||
#bibliography(("/works.bib", "/works.bib"))
|
||||
// Error: 15-55 duplicate bibliography keys: arrgh, distress, glacier-melt, issue201, mcintosh_anxiety, netwok, psychology25, quark, restful, sharing, tolkien54
|
||||
#bibliography(("/files/works.bib", "/files/works.bib"))
|
||||
|
||||
---
|
||||
#set page(width: 200pt)
|
||||
@ -35,4 +40,4 @@ And quark! @quark
|
||||
|
||||
= Multiple Bibs
|
||||
Now we have multiple bibliographies containing #cite("glacier-melt", "keshav2007read")
|
||||
#bibliography(("/works.bib", "/works_too.bib"))
|
||||
#bibliography(("/files/works.bib", "/files/works_too.bib"))
|
||||
|
@ -2,4 +2,4 @@ Hello @netwok
|
||||
And again: @netwok
|
||||
|
||||
#pagebreak()
|
||||
#bibliography("/works.bib", style: "chicago-notes")
|
||||
#bibliography("/files/works.bib", style: "chicago-notes")
|
||||
|
@ -13,7 +13,7 @@ We can clearly see that @fig-cylinder and
|
||||
) <tab-basic>
|
||||
|
||||
#figure(
|
||||
pad(y: -6pt, image("/cylinder.svg", height: 2cm)),
|
||||
pad(y: -6pt, image("/files/cylinder.svg", height: 2cm)),
|
||||
caption: [The basic shapes.],
|
||||
numbering: "I",
|
||||
) <fig-cylinder>
|
||||
@ -30,7 +30,7 @@ We can clearly see that @fig-cylinder and
|
||||
table(
|
||||
columns: 2,
|
||||
[Second cylinder],
|
||||
image("/cylinder.svg"),
|
||||
image("/files/cylinder.svg"),
|
||||
),
|
||||
caption: "A table containing images."
|
||||
) <fig-image-in-table>
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Test footnote in caption.
|
||||
Read the docs #footnote[https://typst.app/docs]!
|
||||
#figure(
|
||||
image("/graph.png", width: 70%),
|
||||
image("/files/graph.png", width: 70%),
|
||||
caption: [
|
||||
A graph #footnote[A _graph_ is a structure with nodes and edges.]
|
||||
]
|
||||
|
@ -12,7 +12,7 @@
|
||||
[This cell
|
||||
#footnote[This footnote is not on the same page]
|
||||
breaks over multiple pages.],
|
||||
image("/tiger.jpg"),
|
||||
image("/files/tiger.jpg"),
|
||||
)
|
||||
|
||||
#table(
|
||||
|
@ -54,7 +54,7 @@ My cool #box(move(dx: 0.7cm, dy: 0.7cm, rotate(10deg, scale(200%, mylink))))
|
||||
// Link containing a block.
|
||||
#link("https://example.com/", block[
|
||||
My cool rhino
|
||||
#box(move(dx: 10pt, image("/rhino.png", width: 1cm)))
|
||||
#box(move(dx: 10pt, image("/files/rhino.png", width: 1cm)))
|
||||
])
|
||||
|
||||
---
|
||||
|
@ -57,4 +57,4 @@
|
||||
// Error: 2-23 cannot outline cite
|
||||
#outline(target: cite)
|
||||
#cite("arrgh", "distress", [p. 22])
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
|
@ -24,7 +24,7 @@
|
||||
})
|
||||
|
||||
#figure(
|
||||
image("/glacier.jpg"),
|
||||
image("/files/glacier.jpg"),
|
||||
caption: [Glacier melting],
|
||||
)
|
||||
|
||||
@ -36,6 +36,6 @@
|
||||
)
|
||||
|
||||
#figure(
|
||||
image("/tiger.jpg"),
|
||||
image("/files/tiger.jpg"),
|
||||
caption: [Tiger world],
|
||||
)
|
||||
|
@ -26,13 +26,13 @@ As seen in @intro, we proceed.
|
||||
|
||||
= Intro
|
||||
#figure(
|
||||
image("/cylinder.svg", height: 1cm),
|
||||
image("/files/cylinder.svg", height: 1cm),
|
||||
caption: [A cylinder.],
|
||||
supplement: "Fig",
|
||||
) <fig1>
|
||||
|
||||
#figure(
|
||||
image("/tiger.jpg", height: 1cm),
|
||||
image("/files/tiger.jpg", height: 1cm),
|
||||
caption: [A tiger.],
|
||||
supplement: "Tig",
|
||||
) <fig2>
|
||||
|
@ -1,12 +1,12 @@
|
||||
// Test baseline handling.
|
||||
|
||||
---
|
||||
Hi #text(1.5em)[You], #text(0.75em)[how are you?]
|
||||
|
||||
Our cockatoo was one of the
|
||||
#text(baseline: -0.2em)[#box(circle(radius: 2pt)) first]
|
||||
#text(baseline: 0.2em)[birds #box(circle(radius: 2pt))]
|
||||
that ever learned to mimic a human voice.
|
||||
|
||||
---
|
||||
Hey #box(baseline: 40%, image("/tiger.jpg", width: 1.5cm)) there!
|
||||
// Test baseline handling.
|
||||
|
||||
---
|
||||
Hi #text(1.5em)[You], #text(0.75em)[how are you?]
|
||||
|
||||
Our cockatoo was one of the
|
||||
#text(baseline: -0.2em)[#box(circle(radius: 2pt)) first]
|
||||
#text(baseline: 0.2em)[birds #box(circle(radius: 2pt))]
|
||||
that ever learned to mimic a human voice.
|
||||
|
||||
---
|
||||
Hey #box(baseline: 40%, image("/files/tiger.jpg", width: 1.5cm)) there!
|
||||
|
@ -7,7 +7,7 @@
|
||||
They can look for the details in @netwok,
|
||||
which is the authoritative source.
|
||||
|
||||
#bibliography("/works.bib")
|
||||
#bibliography("/files/works.bib")
|
||||
|
||||
---
|
||||
// Test punctuation after math equations.
|
||||
|
@ -4,24 +4,24 @@
|
||||
// Test loading different image formats.
|
||||
|
||||
// Load an RGBA PNG image.
|
||||
#image("/rhino.png")
|
||||
#image("/files/rhino.png")
|
||||
|
||||
// Load an RGB JPEG image.
|
||||
#set page(height: 60pt)
|
||||
#image("/tiger.jpg")
|
||||
#image("../../files/tiger.jpg")
|
||||
|
||||
---
|
||||
// Test configuring the size and fitting behaviour of images.
|
||||
|
||||
// Set width and height explicitly.
|
||||
#box(image("/rhino.png", width: 30pt))
|
||||
#box(image("/rhino.png", height: 30pt))
|
||||
#box(image("/files/rhino.png", width: 30pt))
|
||||
#box(image("/files/rhino.png", height: 30pt))
|
||||
|
||||
// Set width and height explicitly and force stretching.
|
||||
#image("/monkey.svg", width: 100%, height: 20pt, fit: "stretch")
|
||||
#image("/files/monkey.svg", width: 100%, height: 20pt, fit: "stretch")
|
||||
|
||||
// Make sure the bounding-box of the image is correct.
|
||||
#align(bottom + right, image("/tiger.jpg", width: 40pt, alt: "A tiger"))
|
||||
#align(bottom + right, image("/files/tiger.jpg", width: 40pt, alt: "A tiger"))
|
||||
|
||||
---
|
||||
// Test all three fit modes.
|
||||
@ -30,24 +30,24 @@
|
||||
columns: (1fr, 1fr, 1fr),
|
||||
rows: 100%,
|
||||
gutter: 3pt,
|
||||
image("/tiger.jpg", width: 100%, height: 100%, fit: "contain"),
|
||||
image("/tiger.jpg", width: 100%, height: 100%, fit: "cover"),
|
||||
image("/monkey.svg", width: 100%, height: 100%, fit: "stretch"),
|
||||
image("/files/tiger.jpg", width: 100%, height: 100%, fit: "contain"),
|
||||
image("/files/tiger.jpg", width: 100%, height: 100%, fit: "cover"),
|
||||
image("/files/monkey.svg", width: 100%, height: 100%, fit: "stretch"),
|
||||
)
|
||||
|
||||
---
|
||||
// Does not fit to remaining height of page.
|
||||
#set page(height: 60pt)
|
||||
Stuff
|
||||
#image("/rhino.png")
|
||||
#image("/files/rhino.png")
|
||||
|
||||
---
|
||||
// Test baseline.
|
||||
A #box(image("/tiger.jpg", height: 1cm, width: 80%)) B
|
||||
A #box(image("/files/tiger.jpg", height: 1cm, width: 80%)) B
|
||||
|
||||
---
|
||||
// Test advanced SVG features.
|
||||
#image("/pattern.svg")
|
||||
#image("/files/pattern.svg")
|
||||
|
||||
---
|
||||
// Error: 8-29 file not found (searched at typ/visualize/path/does/not/exist)
|
||||
@ -58,5 +58,5 @@ A #box(image("/tiger.jpg", height: 1cm, width: 80%)) B
|
||||
#image("./image.typ")
|
||||
|
||||
---
|
||||
// Error: 2-19 failed to parse svg: found closing tag 'g' instead of 'style' in line 4
|
||||
#image("/bad.svg")
|
||||
// Error: 2-25 failed to parse svg: found closing tag 'g' instead of 'style' in line 4
|
||||
#image("/files/bad.svg")
|
||||
|
@ -4,6 +4,6 @@
|
||||
#set page(width: 250pt)
|
||||
|
||||
#figure(
|
||||
image("/diagram.svg"),
|
||||
image("/files/diagram.svg"),
|
||||
caption: [A textful diagram],
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user