mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Tracing-based expression tooltips
This commit is contained in:
parent
c56299c6bd
commit
43ef60c09c
@ -112,6 +112,7 @@ pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
|
||||
let source = Source::synthesized(text, span);
|
||||
let route = model::Route::default();
|
||||
let module = model::eval(vm.world(), route.track(), &source)?;
|
||||
let mut tracer = model::Tracer::default();
|
||||
let module = model::eval(vm.world(), route.track(), tracer.track_mut(), &source)?;
|
||||
Ok(Value::Content(module.content()))
|
||||
}
|
||||
|
38
src/ide/analyze.rs
Normal file
38
src/ide/analyze.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use comemo::Track;
|
||||
|
||||
use crate::model::{eval, Route, Tracer, Value};
|
||||
use crate::syntax::{ast, LinkedNode, SyntaxKind};
|
||||
use crate::World;
|
||||
|
||||
/// Try to determine a set of possible values for an expression.
|
||||
pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
|
||||
match node.cast::<ast::Expr>() {
|
||||
Some(ast::Expr::Ident(_) | ast::Expr::MathIdent(_)) => {
|
||||
if let Some(parent) = node.parent() {
|
||||
if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 {
|
||||
return analyze(world, parent);
|
||||
}
|
||||
}
|
||||
|
||||
let span = node.span();
|
||||
let source = world.source(span.source());
|
||||
let route = Route::default();
|
||||
let mut tracer = Tracer::new(Some(span));
|
||||
eval(world.track(), route.track(), tracer.track_mut(), source).ok();
|
||||
return tracer.finish();
|
||||
}
|
||||
|
||||
Some(ast::Expr::FieldAccess(access)) => {
|
||||
if let Some(child) = node.children().next() {
|
||||
return analyze(world, &child)
|
||||
.into_iter()
|
||||
.filter_map(|target| target.field(&access.field()).ok())
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
//! Capabilities for IDE support.
|
||||
|
||||
mod analyze;
|
||||
mod complete;
|
||||
mod highlight;
|
||||
mod tooltip;
|
||||
|
||||
pub use analyze::*;
|
||||
pub use complete::*;
|
||||
pub use highlight::*;
|
||||
pub use tooltip::*;
|
||||
|
@ -1,40 +1,71 @@
|
||||
use if_chain::if_chain;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{plain_docs_sentence, summarize_font_family};
|
||||
use crate::model::{CastInfo, Value};
|
||||
use super::{analyze, plain_docs_sentence, summarize_font_family};
|
||||
use crate::model::{CastInfo, Tracer, Value};
|
||||
use crate::syntax::ast;
|
||||
use crate::syntax::{LinkedNode, Source, SyntaxKind};
|
||||
use crate::World;
|
||||
|
||||
/// Describe the item under the cursor.
|
||||
pub fn tooltip(world: &dyn World, source: &Source, cursor: usize) -> Option<String> {
|
||||
pub fn tooltip(
|
||||
world: &(dyn World + 'static),
|
||||
source: &Source,
|
||||
cursor: usize,
|
||||
) -> Option<String> {
|
||||
let leaf = LinkedNode::new(source.root()).leaf_at(cursor)?;
|
||||
|
||||
function_tooltip(world, &leaf)
|
||||
.or_else(|| named_param_tooltip(world, &leaf))
|
||||
named_param_tooltip(world, &leaf)
|
||||
.or_else(|| font_family_tooltip(world, &leaf))
|
||||
.or_else(|| expr_tooltip(world, &leaf))
|
||||
}
|
||||
|
||||
/// Tooltip for a function or set rule name.
|
||||
fn function_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<String> {
|
||||
if_chain! {
|
||||
if let Some(ident) = leaf.cast::<ast::Ident>();
|
||||
if matches!(
|
||||
leaf.parent_kind(),
|
||||
Some(SyntaxKind::FuncCall | SyntaxKind::SetRule),
|
||||
);
|
||||
if let Some(Value::Func(func)) = world.library().global.scope().get(&ident);
|
||||
if let Some(info) = func.info();
|
||||
then {
|
||||
return Some(plain_docs_sentence(info.docs));
|
||||
/// Tooltip for a hovered expression.
|
||||
fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<String> {
|
||||
if !leaf.is::<ast::Expr>() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let values = analyze(world, leaf);
|
||||
if let [value] = values.as_slice() {
|
||||
if let Some(docs) = value.docs() {
|
||||
return Some(plain_docs_sentence(docs));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
let mut tooltip = String::new();
|
||||
let mut iter = values.into_iter().enumerate();
|
||||
for (i, value) in (&mut iter).take(Tracer::MAX - 1) {
|
||||
if i > 0 && !tooltip.is_empty() {
|
||||
tooltip.push_str(", ");
|
||||
}
|
||||
let repr = value.repr();
|
||||
let repr = repr.as_str();
|
||||
let len = repr.len();
|
||||
if len <= 40 {
|
||||
tooltip.push_str(repr);
|
||||
} else {
|
||||
let mut graphemes = repr.graphemes(true);
|
||||
let r = graphemes.next_back().map_or(0, str::len);
|
||||
let l = graphemes.take(40).map(str::len).sum();
|
||||
tooltip.push_str(&repr[..l]);
|
||||
tooltip.push_str("...");
|
||||
tooltip.push_str(&repr[len - r..]);
|
||||
}
|
||||
}
|
||||
|
||||
if iter.next().is_some() {
|
||||
tooltip.push_str(", ...");
|
||||
}
|
||||
|
||||
(!tooltip.is_empty()).then(|| tooltip)
|
||||
}
|
||||
|
||||
/// Tooltips for components of a named parameter.
|
||||
fn named_param_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<String> {
|
||||
fn named_param_tooltip(
|
||||
world: &(dyn World + 'static),
|
||||
leaf: &LinkedNode,
|
||||
) -> Option<String> {
|
||||
let (info, named) = if_chain! {
|
||||
// Ensure that we are in a named pair in the arguments to a function
|
||||
// call or set rule.
|
||||
@ -92,7 +123,10 @@ fn find_string_doc(info: &CastInfo, string: &str) -> Option<&'static str> {
|
||||
}
|
||||
|
||||
/// Tooltip for font family.
|
||||
fn font_family_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<String> {
|
||||
fn font_family_tooltip(
|
||||
world: &(dyn World + 'static),
|
||||
leaf: &LinkedNode,
|
||||
) -> Option<String> {
|
||||
if_chain! {
|
||||
// Ensure that we are on top of a string.
|
||||
if let Some(string) = leaf.cast::<ast::Str>();
|
||||
|
@ -51,6 +51,7 @@ pub mod syntax;
|
||||
use std::path::Path;
|
||||
|
||||
use comemo::{Prehashed, Track};
|
||||
use model::Tracer;
|
||||
|
||||
use crate::diag::{FileResult, SourceResult};
|
||||
use crate::doc::Document;
|
||||
@ -63,7 +64,8 @@ use crate::util::Buffer;
|
||||
pub fn compile(world: &(dyn World + 'static), source: &Source) -> SourceResult<Document> {
|
||||
// Evaluate the source file into a module.
|
||||
let route = Route::default();
|
||||
let module = model::eval(world.track(), route.track(), source)?;
|
||||
let mut tracer = Tracer::default();
|
||||
let module = model::eval(world.track(), route.track(), tracer.track_mut(), source)?;
|
||||
|
||||
// Typeset the module's contents.
|
||||
model::typeset(world.track(), &module.content())
|
||||
|
@ -136,7 +136,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Return the first matching element.
|
||||
pub fn find(&self, vm: &Vm, func: Func) -> SourceResult<Option<Value>> {
|
||||
pub fn find(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<Value>> {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
@ -151,7 +151,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Return the index of the first matching element.
|
||||
pub fn position(&self, vm: &Vm, func: Func) -> SourceResult<Option<i64>> {
|
||||
pub fn position(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<i64>> {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
@ -167,7 +167,7 @@ impl Array {
|
||||
|
||||
/// Return a new array with only those elements for which the function
|
||||
/// returns true.
|
||||
pub fn filter(&self, vm: &Vm, func: Func) -> SourceResult<Self> {
|
||||
pub fn filter(&self, vm: &mut Vm, func: Func) -> SourceResult<Self> {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
@ -182,7 +182,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Transform each item in the array with a function.
|
||||
pub fn map(&self, vm: &Vm, func: Func) -> SourceResult<Self> {
|
||||
pub fn map(&self, vm: &mut Vm, func: Func) -> SourceResult<Self> {
|
||||
if func.argc().map_or(false, |count| !(1..=2).contains(&count)) {
|
||||
bail!(func.span(), "function must have one or two parameters");
|
||||
}
|
||||
@ -201,7 +201,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Fold all of the array's elements into one with a function.
|
||||
pub fn fold(&self, vm: &Vm, init: Value, func: Func) -> SourceResult<Value> {
|
||||
pub fn fold(&self, vm: &mut Vm, init: Value, func: Func) -> SourceResult<Value> {
|
||||
if func.argc().map_or(false, |count| count != 2) {
|
||||
bail!(func.span(), "function must have exactly two parameters");
|
||||
}
|
||||
@ -214,7 +214,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Whether any element matches.
|
||||
pub fn any(&self, vm: &Vm, func: Func) -> SourceResult<bool> {
|
||||
pub fn any(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
@ -229,7 +229,7 @@ impl Array {
|
||||
}
|
||||
|
||||
/// Whether all elements match.
|
||||
pub fn all(&self, vm: &Vm, func: Func) -> SourceResult<bool> {
|
||||
pub fn all(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl Dict {
|
||||
}
|
||||
|
||||
/// Transform each pair in the dictionary with a function.
|
||||
pub fn map(&self, vm: &Vm, func: Func) -> SourceResult<Array> {
|
||||
pub fn map(&self, vm: &mut Vm, func: Func) -> SourceResult<Array> {
|
||||
if func.argc().map_or(false, |count| count != 2) {
|
||||
bail!(func.span(), "function must have exactly two parameters");
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use comemo::{Track, Tracked};
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{
|
||||
@ -17,7 +17,7 @@ use crate::diag::{
|
||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
||||
use crate::syntax::ast::AstNode;
|
||||
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode};
|
||||
use crate::util::{EcoString, PathExt};
|
||||
use crate::util::PathExt;
|
||||
use crate::World;
|
||||
|
||||
const MAX_ITERATIONS: usize = 10_000;
|
||||
@ -28,6 +28,7 @@ const MAX_CALL_DEPTH: usize = 256;
|
||||
pub fn eval(
|
||||
world: Tracked<dyn World>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
source: &Source,
|
||||
) -> SourceResult<Module> {
|
||||
// Prevent cyclic evaluation.
|
||||
@ -44,7 +45,7 @@ pub fn eval(
|
||||
// Evaluate the module.
|
||||
let route = unsafe { Route::insert(route, id) };
|
||||
let scopes = Scopes::new(Some(library));
|
||||
let mut vm = Vm::new(world, route.track(), id, scopes, 0);
|
||||
let mut vm = Vm::new(world, route.track(), tracer, id, scopes, 0);
|
||||
let result = source.ast()?.eval(&mut vm);
|
||||
|
||||
// Handle control flow.
|
||||
@ -68,6 +69,8 @@ pub struct Vm<'a> {
|
||||
pub(super) items: LangItems,
|
||||
/// The route of source ids the VM took to reach its current location.
|
||||
pub(super) route: Tracked<'a, Route>,
|
||||
/// The tracer for inspection of the values an expression produces.
|
||||
pub(super) tracer: TrackedMut<'a, Tracer>,
|
||||
/// The current location.
|
||||
pub(super) location: SourceId,
|
||||
/// A control flow event that is currently happening.
|
||||
@ -76,6 +79,8 @@ pub struct Vm<'a> {
|
||||
pub(super) scopes: Scopes<'a>,
|
||||
/// The current call depth.
|
||||
pub(super) depth: usize,
|
||||
/// A span that is currently traced.
|
||||
pub(super) traced: Option<Span>,
|
||||
}
|
||||
|
||||
impl<'a> Vm<'a> {
|
||||
@ -83,18 +88,22 @@ impl<'a> Vm<'a> {
|
||||
pub(super) fn new(
|
||||
world: Tracked<'a, dyn World>,
|
||||
route: Tracked<'a, Route>,
|
||||
tracer: TrackedMut<'a, Tracer>,
|
||||
location: SourceId,
|
||||
scopes: Scopes<'a>,
|
||||
depth: usize,
|
||||
) -> Self {
|
||||
let traced = tracer.span(location);
|
||||
Self {
|
||||
world,
|
||||
items: world.library().items.clone(),
|
||||
route,
|
||||
tracer,
|
||||
location,
|
||||
flow: None,
|
||||
scopes,
|
||||
depth,
|
||||
traced,
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +112,15 @@ impl<'a> Vm<'a> {
|
||||
self.world
|
||||
}
|
||||
|
||||
/// Define a variable in the current scope.
|
||||
pub fn define(&mut self, var: ast::Ident, value: impl Into<Value>) {
|
||||
let value = value.into();
|
||||
if self.traced == Some(var.span()) {
|
||||
self.tracer.trace(value.clone());
|
||||
}
|
||||
self.scopes.top.define(var.take(), value);
|
||||
}
|
||||
|
||||
/// Resolve a user-entered path to be relative to the compilation
|
||||
/// environment's root.
|
||||
pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
|
||||
@ -182,6 +200,47 @@ impl Route {
|
||||
}
|
||||
}
|
||||
|
||||
/// Traces which values existed for the expression with the given span.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Tracer {
|
||||
span: Option<Span>,
|
||||
values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Tracer {
|
||||
/// The maximum number of traced items.
|
||||
pub const MAX: usize = 10;
|
||||
|
||||
/// Create a new tracer, possibly with a span under inspection.
|
||||
pub fn new(span: Option<Span>) -> Self {
|
||||
Self { span, values: vec![] }
|
||||
}
|
||||
|
||||
/// Get the traced values.
|
||||
pub fn finish(self) -> Vec<Value> {
|
||||
self.values
|
||||
}
|
||||
}
|
||||
|
||||
#[comemo::track]
|
||||
impl Tracer {
|
||||
/// The traced span if it is part of the given source file.
|
||||
fn span(&self, id: SourceId) -> Option<Span> {
|
||||
if self.span.map(Span::source) == Some(id) {
|
||||
self.span
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace a value for the span.
|
||||
fn trace(&mut self, v: Value) {
|
||||
if self.values.len() < Self::MAX {
|
||||
self.values.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate an expression.
|
||||
pub(super) trait Eval {
|
||||
/// The output of evaluating the expression.
|
||||
@ -259,12 +318,11 @@ impl Eval for ast::Expr {
|
||||
error!(span, "{} is only allowed directly in code and content blocks", name)
|
||||
};
|
||||
|
||||
match self {
|
||||
let v = match self {
|
||||
Self::Text(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Space(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Linebreak(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Parbreak(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Symbol(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Escape(v) => v.eval(vm),
|
||||
Self::Shorthand(v) => v.eval(vm),
|
||||
Self::SmartQuote(v) => v.eval(vm).map(Value::Content),
|
||||
@ -319,6 +377,8 @@ impl Eval for ast::Expr {
|
||||
}?
|
||||
.spanned(span);
|
||||
|
||||
if vm.traced == Some(span) {
|
||||
vm.tracer.trace(v.clone());
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
@ -1049,7 +1109,7 @@ impl Eval for ast::LetBinding {
|
||||
Some(expr) => expr.eval(vm)?,
|
||||
None => Value::None,
|
||||
};
|
||||
vm.scopes.top.define(self.binding().take(), value);
|
||||
vm.define(self.binding(), value);
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
@ -1183,7 +1243,7 @@ impl Eval for ast::ForLoop {
|
||||
|
||||
#[allow(unused_parens)]
|
||||
for ($($value),*) in $iter {
|
||||
$(vm.scopes.top.define($binding.clone(), $value);)*
|
||||
$(vm.define($binding.clone(), $value);)*
|
||||
|
||||
let body = self.body();
|
||||
let value = body.eval(vm)?;
|
||||
@ -1206,8 +1266,8 @@ impl Eval for ast::ForLoop {
|
||||
|
||||
let iter = self.iter().eval(vm)?;
|
||||
let pattern = self.pattern();
|
||||
let key = pattern.key().map(ast::Ident::take);
|
||||
let value = pattern.value().take();
|
||||
let key = pattern.key();
|
||||
let value = pattern.value();
|
||||
|
||||
match (key, value, iter) {
|
||||
(None, v, Value::Str(string)) => {
|
||||
@ -1271,7 +1331,7 @@ impl Eval for ast::ModuleImport {
|
||||
let mut errors = vec![];
|
||||
for ident in idents {
|
||||
if let Some(value) = module.scope().get(&ident) {
|
||||
vm.scopes.top.define(ident.take(), value.clone());
|
||||
vm.define(ident, value.clone());
|
||||
} else {
|
||||
errors.push(error!(ident.span(), "unresolved import"));
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
||||
use comemo::{Track, Tracked};
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
|
||||
use super::{
|
||||
Args, CastInfo, Dict, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector,
|
||||
StyleMap, Value, Vm,
|
||||
StyleMap, Tracer, Value, Vm,
|
||||
};
|
||||
use crate::diag::{bail, SourceResult, StrResult};
|
||||
use crate::syntax::ast::{self, AstNode, Expr};
|
||||
@ -110,7 +110,7 @@ impl Func {
|
||||
}
|
||||
|
||||
/// Call the function with the given arguments.
|
||||
pub fn call(&self, vm: &Vm, mut args: Args) -> SourceResult<Value> {
|
||||
pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> {
|
||||
let value = match self.0.as_ref() {
|
||||
Repr::Native(native) => (native.func)(vm, &mut args)?,
|
||||
Repr::Closure(closure) => closure.call(vm, self, &mut args)?,
|
||||
@ -132,8 +132,9 @@ impl Func {
|
||||
let route = Route::default();
|
||||
let id = SourceId::detached();
|
||||
let scopes = Scopes::new(None);
|
||||
let vm = Vm::new(world, route.track(), id, scopes, 0);
|
||||
self.call(&vm, args)
|
||||
let mut tracer = Tracer::default();
|
||||
let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes, 0);
|
||||
self.call(&mut vm, args)
|
||||
}
|
||||
|
||||
/// Apply the given arguments to the function.
|
||||
@ -292,7 +293,7 @@ pub(super) struct Closure {
|
||||
|
||||
impl Closure {
|
||||
/// Call the function in the context with the arguments.
|
||||
fn call(&self, vm: &Vm, this: &Func, args: &mut Args) -> SourceResult<Value> {
|
||||
fn call(&self, vm: &mut Vm, this: &Func, args: &mut Args) -> SourceResult<Value> {
|
||||
// Don't leak the scopes from the call site. Instead, we use the scope
|
||||
// of captured variables we collected earlier.
|
||||
let mut scopes = Scopes::new(None);
|
||||
@ -327,7 +328,14 @@ impl Closure {
|
||||
let route = if detached { fresh.track() } else { vm.route };
|
||||
|
||||
// Evaluate the body.
|
||||
let mut sub = Vm::new(vm.world, route, self.location, scopes, vm.depth + 1);
|
||||
let mut sub = Vm::new(
|
||||
vm.world,
|
||||
route,
|
||||
TrackedMut::reborrow_mut(&mut vm.tracer),
|
||||
self.location,
|
||||
scopes,
|
||||
vm.depth + 1,
|
||||
);
|
||||
let result = self.body.eval(&mut sub);
|
||||
|
||||
// Handle control flow.
|
||||
|
@ -7,7 +7,7 @@ use crate::util::EcoString;
|
||||
|
||||
/// Call a method on a value.
|
||||
pub fn call(
|
||||
vm: &Vm,
|
||||
vm: &mut Vm,
|
||||
value: Value,
|
||||
method: &str,
|
||||
mut args: Args,
|
||||
|
@ -155,6 +155,14 @@ impl Value {
|
||||
_ => self.display(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to extract documentation for the value.
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Func(func) => func.info().map(|info| info.docs),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
|
@ -60,13 +60,29 @@ fn bench_edit(iai: &mut Iai) {
|
||||
fn bench_eval(iai: &mut Iai) {
|
||||
let world = BenchWorld::new();
|
||||
let route = typst::model::Route::default();
|
||||
iai.run(|| typst::model::eval(world.track(), route.track(), &world.source).unwrap());
|
||||
let mut tracer = typst::model::Tracer::default();
|
||||
iai.run(|| {
|
||||
typst::model::eval(
|
||||
world.track(),
|
||||
route.track(),
|
||||
tracer.track_mut(),
|
||||
&world.source,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_typeset(iai: &mut Iai) {
|
||||
let world = BenchWorld::new();
|
||||
let route = typst::model::Route::default();
|
||||
let module = typst::model::eval(world.track(), route.track(), &world.source).unwrap();
|
||||
let mut tracer = typst::model::Tracer::default();
|
||||
let module = typst::model::eval(
|
||||
world.track(),
|
||||
route.track(),
|
||||
tracer.track_mut(),
|
||||
&world.source,
|
||||
)
|
||||
.unwrap();
|
||||
let content = module.content();
|
||||
iai.run(|| typst::model::typeset(world.track(), &content));
|
||||
}
|
||||
|
@ -435,7 +435,9 @@ fn test_part(
|
||||
if world.print.model {
|
||||
let world = (world as &dyn World).track();
|
||||
let route = typst::model::Route::default();
|
||||
let module = typst::model::eval(world, route.track(), source).unwrap();
|
||||
let mut tracer = typst::model::Tracer::default();
|
||||
let module =
|
||||
typst::model::eval(world, route.track(), tracer.track_mut(), source).unwrap();
|
||||
println!("Model:\n{:#?}\n", module.content());
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user