mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
90 lines
2.6 KiB
Rust
90 lines
2.6 KiB
Rust
use std::fs::File;
|
|
use std::io::BufWriter;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use typst::diag::{bail, StrResult};
|
|
use typst::syntax::Span;
|
|
use typst::World;
|
|
|
|
use crate::args::{CliArguments, Command};
|
|
use crate::world::SystemWorld;
|
|
|
|
/// Allows to record timings of function executions.
|
|
pub struct Timer {
|
|
/// Where to save the recorded timings of each compilation step.
|
|
path: Option<PathBuf>,
|
|
/// The current watch iteration.
|
|
index: usize,
|
|
}
|
|
|
|
impl Timer {
|
|
/// Initializes the timing system and returns a timer that can be used to
|
|
/// record timings for a specific function invocation.
|
|
pub fn new(args: &CliArguments) -> Timer {
|
|
let record = match &args.command {
|
|
Command::Compile(command) => command.timings.clone(),
|
|
Command::Watch(command) => command.timings.clone(),
|
|
_ => None,
|
|
};
|
|
|
|
// Enable event collection.
|
|
if record.is_some() {
|
|
typst_timing::enable();
|
|
}
|
|
|
|
let path =
|
|
record.map(|path| path.unwrap_or_else(|| PathBuf::from("record-{n}.json")));
|
|
|
|
Timer { path, index: 0 }
|
|
}
|
|
|
|
/// Records all timings in `f` and writes them to disk.
|
|
pub fn record<T>(
|
|
&mut self,
|
|
world: &mut SystemWorld,
|
|
f: impl FnOnce(&mut SystemWorld) -> T,
|
|
) -> StrResult<T> {
|
|
let Some(path) = &self.path else {
|
|
return Ok(f(world));
|
|
};
|
|
|
|
typst_timing::clear();
|
|
|
|
let string = path.to_str().unwrap_or_default();
|
|
let numbered = string.contains("{n}");
|
|
if !numbered && self.index > 0 {
|
|
bail!("cannot export multiple recordings without `{{n}}` in path");
|
|
}
|
|
|
|
let storage;
|
|
let path = if numbered {
|
|
storage = string.replace("{n}", &self.index.to_string());
|
|
Path::new(&storage)
|
|
} else {
|
|
path.as_path()
|
|
};
|
|
|
|
let output = f(world);
|
|
self.index += 1;
|
|
|
|
let file =
|
|
File::create(path).map_err(|e| format!("failed to create file: {e}"))?;
|
|
let writer = BufWriter::with_capacity(1 << 20, file);
|
|
|
|
typst_timing::export_json(writer, |span| {
|
|
resolve_span(world, span).unwrap_or_else(|| ("unknown".to_string(), 0))
|
|
})?;
|
|
|
|
Ok(output)
|
|
}
|
|
}
|
|
|
|
/// Turns a span into a (file, line) pair.
|
|
fn resolve_span(world: &SystemWorld, span: Span) -> Option<(String, u32)> {
|
|
let id = span.id()?;
|
|
let source = world.source(id).ok()?;
|
|
let range = source.range(span)?;
|
|
let line = source.byte_to_line(range.start)?;
|
|
Some((format!("{id:?}"), line as u32 + 1))
|
|
}
|