summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-17 16:27:40 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-17 18:18:47 +0100
commitaf7fe4d76083c597ec2198a73383b9e3899d75ea (patch)
treecb3b9766ee15dbfbecf7f9b9a2257859d7a7b3c7 /src
parent6d64d3e8e9123f3fa8166c8b710e2b2c61ed5898 (diff)
Hover and autocomplete in show rules
Diffstat (limited to 'src')
-rw-r--r--src/eval/array.rs14
-rw-r--r--src/eval/func.rs33
-rw-r--r--src/eval/mod.rs33
-rw-r--r--src/ide/analyze.rs20
-rw-r--r--src/lib.rs22
-rw-r--r--src/model/content.rs10
-rw-r--r--src/model/realize.rs8
-rw-r--r--src/model/styles.rs32
-rw-r--r--src/model/typeset.rs19
9 files changed, 113 insertions, 78 deletions
diff --git a/src/eval/array.rs b/src/eval/array.rs
index 979f15d4..e42fd28d 100644
--- a/src/eval/array.rs
+++ b/src/eval/array.rs
@@ -144,7 +144,7 @@ impl Array {
}
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
- if func.call(vm, args)?.cast::<bool>().at(func.span())? {
+ if func.call_vm(vm, args)?.cast::<bool>().at(func.span())? {
return Ok(Some(item.clone()));
}
}
@@ -158,7 +158,7 @@ impl Array {
}
for (i, item) in self.iter().enumerate() {
let args = Args::new(func.span(), [item.clone()]);
- if func.call(vm, args)?.cast::<bool>().at(func.span())? {
+ if func.call_vm(vm, args)?.cast::<bool>().at(func.span())? {
return Ok(Some(i as i64));
}
}
@@ -175,7 +175,7 @@ impl Array {
let mut kept = EcoVec::new();
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
- if func.call(vm, args)?.cast::<bool>().at(func.span())? {
+ if func.call_vm(vm, args)?.cast::<bool>().at(func.span())? {
kept.push(item.clone())
}
}
@@ -196,7 +196,7 @@ impl Array {
args.push(func.span(), Value::Int(i as i64));
}
args.push(func.span(), item.clone());
- func.call(vm, args)
+ func.call_vm(vm, args)
})
.collect()
}
@@ -209,7 +209,7 @@ impl Array {
let mut acc = init;
for item in self.iter() {
let args = Args::new(func.span(), [acc, item.clone()]);
- acc = func.call(vm, args)?;
+ acc = func.call_vm(vm, args)?;
}
Ok(acc)
}
@@ -221,7 +221,7 @@ impl Array {
}
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
- if func.call(vm, args)?.cast::<bool>().at(func.span())? {
+ if func.call_vm(vm, args)?.cast::<bool>().at(func.span())? {
return Ok(true);
}
}
@@ -236,7 +236,7 @@ impl Array {
}
for item in self.iter() {
let args = Args::new(func.span(), [item.clone()]);
- if !func.call(vm, args)?.cast::<bool>().at(func.span())? {
+ if !func.call_vm(vm, args)?.cast::<bool>().at(func.span())? {
return Ok(false);
}
}
diff --git a/src/eval/func.rs b/src/eval/func.rs
index a5fa6fa1..c3ec4233 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -13,7 +13,7 @@ use super::{
Vm,
};
use crate::diag::{bail, SourceResult, StrResult};
-use crate::model::{NodeId, Selector, StyleMap};
+use crate::model::{NodeId, Selector, StyleMap, Vt};
use crate::syntax::ast::{self, AstNode, Expr};
use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::util::hash128;
@@ -82,7 +82,7 @@ impl Func {
}
/// Call the function with the given arguments.
- pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> {
+ pub fn call_vm(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> {
match &**self.0 {
Repr::Native(native) => {
let value = (native.func)(vm, &mut args)?;
@@ -111,23 +111,29 @@ impl Func {
}
Repr::With(wrapped, applied) => {
args.items = applied.items.iter().cloned().chain(args.items).collect();
- return wrapped.call(vm, args);
+ return wrapped.call_vm(vm, args);
}
}
}
- /// Call the function without an existing virtual machine.
- pub fn call_detached(
+ /// Call the function with a Vt.
+ pub fn call_vt(
&self,
- world: Tracked<dyn World>,
- args: Args,
+ vt: &mut Vt,
+ args: impl IntoIterator<Item = Value>,
) -> SourceResult<Value> {
let route = Route::default();
let id = SourceId::detached();
let scopes = Scopes::new(None);
- let mut tracer = Tracer::default();
- let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes, 0);
- self.call(&mut vm, args)
+ let mut vm = Vm::new(
+ vt.world,
+ route.track(),
+ TrackedMut::reborrow_mut(&mut vt.tracer),
+ id,
+ scopes,
+ );
+ let args = Args::new(self.span(), args);
+ self.call_vm(&mut vm, args)
}
/// Apply the given arguments to the function.
@@ -208,7 +214,7 @@ impl From<NodeId> for Func {
/// A native Rust function.
pub struct NativeFunc {
/// The function's implementation.
- pub func: fn(&Vm, &mut Args) -> SourceResult<Value>,
+ pub func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
/// Details about the function.
pub info: Lazy<FuncInfo>,
}
@@ -352,10 +358,11 @@ impl Closure {
args.finish()?;
// Evaluate the body.
- let mut sub = Vm::new(world, route, tracer, closure.location, scopes, depth);
- let result = closure.body.eval(&mut sub);
+ let mut sub = Vm::new(world, route, tracer, closure.location, scopes);
+ sub.depth = depth;
// Handle control flow.
+ let result = closure.body.eval(&mut sub);
match sub.flow {
Some(Flow::Return(_, Some(explicit))) => return Ok(explicit),
Some(Flow::Return(_, None)) => {}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 127c930f..1c002c49 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -81,7 +81,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(), tracer, id, scopes, 0);
+ let mut vm = Vm::new(world, route.track(), tracer, id, scopes);
let root = match source.root().cast::<ast::Markup>() {
Some(markup) if vm.traced.is_some() => markup,
_ => source.ast()?,
@@ -121,7 +121,7 @@ pub fn eval_code_str(
let scopes = Scopes::new(Some(library));
let route = Route::default();
let mut tracer = Tracer::default();
- let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes, 0);
+ let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes);
let code = root.cast::<ast::Code>().unwrap();
let result = code.eval(&mut vm);
@@ -139,34 +139,33 @@ pub fn eval_code_str(
/// virtual machine is created for each module evaluation and function call.
pub struct Vm<'a> {
/// The compilation environment.
- pub(super) world: Tracked<'a, dyn World>,
+ world: Tracked<'a, dyn World>,
/// The language items.
- pub(super) items: LangItems,
+ items: LangItems,
/// The route of source ids the VM took to reach its current location.
- pub(super) route: Tracked<'a, Route>,
+ route: Tracked<'a, Route>,
/// The tracer for inspection of the values an expression produces.
- pub(super) tracer: TrackedMut<'a, Tracer>,
+ tracer: TrackedMut<'a, Tracer>,
/// The current location.
- pub(super) location: SourceId,
+ location: SourceId,
/// A control flow event that is currently happening.
- pub(super) flow: Option<Flow>,
+ flow: Option<Flow>,
/// The stack of scopes.
- pub(super) scopes: Scopes<'a>,
+ scopes: Scopes<'a>,
/// The current call depth.
- pub(super) depth: usize,
+ depth: usize,
/// A span that is currently traced.
- pub(super) traced: Option<Span>,
+ traced: Option<Span>,
}
impl<'a> Vm<'a> {
/// Create a new virtual machine.
- pub(super) fn new(
+ 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 {
@@ -177,7 +176,7 @@ impl<'a> Vm<'a> {
location,
flow: None,
scopes,
- depth,
+ depth: 0,
traced,
}
}
@@ -358,7 +357,7 @@ fn eval_markup(
}
let tail = eval_markup(vm, exprs)?;
- seq.push(tail.styled_with_recipe(vm.world, recipe)?)
+ seq.push(tail.styled_with_recipe(vm, recipe)?)
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
@@ -791,7 +790,7 @@ fn eval_code(
}
let tail = eval_code(vm, exprs)?.display();
- Value::Content(tail.styled_with_recipe(vm.world, recipe)?)
+ Value::Content(tail.styled_with_recipe(vm, recipe)?)
}
_ => expr.eval(vm)?,
};
@@ -1053,7 +1052,7 @@ impl Eval for ast::FuncCall {
let callee = callee.cast::<Func>().at(callee_span)?;
let point = || Tracepoint::Call(callee.name().map(Into::into));
- callee.call(vm, args).trace(vm.world, point, span)
+ callee.call_vm(vm, args).trace(vm.world, point, span)
}
}
diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs
index ccb89a9c..68b82b05 100644
--- a/src/ide/analyze.rs
+++ b/src/ide/analyze.rs
@@ -36,11 +36,23 @@ pub fn analyze_expr(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Val
}
}
- 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();
+ let mut tracer = Tracer::new(Some(node.span()));
+ typst::eval::eval(
+ world.track(),
+ route.track(),
+ tracer.track_mut(),
+ world.main(),
+ )
+ .and_then(|module| {
+ typst::model::typeset(
+ world.track(),
+ tracer.track_mut(),
+ &module.content(),
+ )
+ })
+ .ok();
+
tracer.finish()
}
diff --git a/src/lib.rs b/src/lib.rs
index 7cfed897..9dbaf721 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -63,14 +63,15 @@ use crate::syntax::{Source, SourceId};
use crate::util::Buffer;
/// Compile a source file into a fully layouted document.
-pub fn compile(world: &(dyn World + 'static), source: &Source) -> SourceResult<Document> {
+pub fn compile(world: &(dyn World + 'static)) -> SourceResult<Document> {
// Evaluate the source file into a module.
let route = Route::default();
let mut tracer = Tracer::default();
- let module = eval::eval(world.track(), route.track(), tracer.track_mut(), source)?;
+ let module =
+ eval::eval(world.track(), route.track(), tracer.track_mut(), world.main())?;
// Typeset the module's contents.
- model::typeset(world.track(), &module.content())
+ model::typeset(world.track(), tracer.track_mut(), &module.content())
}
/// The environment in which typesetting occurs.
@@ -86,6 +87,15 @@ pub trait World {
/// The standard library.
fn library(&self) -> &Prehashed<Library>;
+ /// The main source file.
+ fn main(&self) -> &Source;
+
+ /// Try to resolve the unique id of a source file.
+ fn resolve(&self, path: &Path) -> FileResult<SourceId>;
+
+ /// Access a source file by id.
+ fn source(&self, id: SourceId) -> &Source;
+
/// Metadata about all known fonts.
fn book(&self) -> &Prehashed<FontBook>;
@@ -94,10 +104,4 @@ pub trait World {
/// Try to access a file at a path.
fn file(&self, path: &Path) -> FileResult<Buffer>;
-
- /// Try to resolve the unique id of a source file.
- fn resolve(&self, path: &Path) -> FileResult<SourceId>;
-
- /// Access a source file by id.
- fn source(&self, id: SourceId) -> &Source;
}
diff --git a/src/model/content.rs b/src/model/content.rs
index bd24829d..5317236e 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -4,7 +4,6 @@ use std::hash::{Hash, Hasher};
use std::iter::{self, Sum};
use std::ops::{Add, AddAssign, Deref};
-use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec};
use once_cell::sync::Lazy;
@@ -19,7 +18,6 @@ use crate::eval::{
};
use crate::syntax::Span;
use crate::util::pretty_array_like;
-use crate::World;
/// Composable representation of styled content.
#[derive(Clone, Hash)]
@@ -219,13 +217,9 @@ impl Content {
}
/// Style this content with a recipe, eagerly applying it if possible.
- pub fn styled_with_recipe(
- self,
- world: Tracked<dyn World>,
- recipe: Recipe,
- ) -> SourceResult<Self> {
+ pub fn styled_with_recipe(self, vm: &mut Vm, recipe: Recipe) -> SourceResult<Self> {
if recipe.selector.is_none() {
- recipe.apply(world, self)
+ recipe.apply_vm(vm, self)
} else {
Ok(self.styled(Style::Recipe(recipe)))
}
diff --git a/src/model/realize.rs b/src/model/realize.rs
index 70c75644..634a31fd 100644
--- a/src/model/realize.rs
+++ b/src/model/realize.rs
@@ -97,7 +97,7 @@ pub fn realize(
/// Try to apply a recipe to the target.
fn try_apply(
- vt: &Vt,
+ vt: &mut Vt,
target: &Content,
recipe: &Recipe,
guard: Guard,
@@ -108,7 +108,7 @@ fn try_apply(
return Ok(None);
}
- recipe.apply(vt.world, target.clone().guarded(guard)).map(Some)
+ recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Label(label)) => {
@@ -116,7 +116,7 @@ fn try_apply(
return Ok(None);
}
- recipe.apply(vt.world, target.clone().guarded(guard)).map(Some)
+ recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Regex(regex)) => {
@@ -140,7 +140,7 @@ fn try_apply(
}
let piece = make(m.as_str().into()).guarded(guard);
- let transformed = recipe.apply(vt.world, piece)?;
+ let transformed = recipe.apply_vt(vt, piece)?;
result.push(transformed);
cursor = m.end();
}
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 359f1461..8cccb5f6 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,15 +1,13 @@
use std::fmt::{self, Debug, Formatter, Write};
use std::iter;
-use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec};
-use super::{Content, Label, Node, NodeId};
+use super::{Content, Label, Node, NodeId, Vt};
use crate::diag::{SourceResult, Trace, Tracepoint};
-use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value};
+use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value, Vm};
use crate::syntax::Span;
use crate::util::pretty_array_like;
-use crate::World;
/// A map of style properties.
#[derive(Default, Clone, Hash)]
@@ -197,20 +195,32 @@ impl Recipe {
}
/// Apply the recipe to the given content.
- pub fn apply(
- &self,
- world: Tracked<dyn World>,
- content: Content,
- ) -> SourceResult<Content> {
+ pub fn apply_vm(&self, vm: &mut Vm, content: Content) -> SourceResult<Content> {
match &self.transform {
Transform::Content(content) => Ok(content.clone()),
Transform::Func(func) => {
let args = Args::new(self.span, [Value::Content(content.clone())]);
- let mut result = func.call_detached(world, args);
+ let mut result = func.call_vm(vm, args);
// For selector-less show rules, a tracepoint makes no sense.
if self.selector.is_some() {
let point = || Tracepoint::Show(content.id().name.into());
- result = result.trace(world, point, content.span());
+ result = result.trace(vm.world(), point, content.span());
+ }
+ Ok(result?.display())
+ }
+ Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())),
+ }
+ }
+
+ /// Apply the recipe to the given content.
+ pub fn apply_vt(&self, vt: &mut Vt, content: Content) -> SourceResult<Content> {
+ match &self.transform {
+ Transform::Content(content) => Ok(content.clone()),
+ Transform::Func(func) => {
+ let mut result = func.call_vt(vt, [Value::Content(content.clone())]);
+ if self.selector.is_some() {
+ let point = || Tracepoint::Show(content.id().name.into());
+ result = result.trace(vt.world, point, content.span());
}
Ok(result?.display())
}
diff --git a/src/model/typeset.rs b/src/model/typeset.rs
index e9cb3d2c..683f2846 100644
--- a/src/model/typeset.rs
+++ b/src/model/typeset.rs
@@ -6,13 +6,18 @@ use comemo::{Constraint, Track, Tracked, TrackedMut};
use super::{Content, Selector, StyleChain};
use crate::diag::SourceResult;
use crate::doc::{Document, Element, Frame, Location, Meta};
+use crate::eval::Tracer;
use crate::geom::{Point, Transform};
use crate::util::NonZeroExt;
use crate::World;
/// Typeset content into a fully layouted document.
#[comemo::memoize]
-pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Document> {
+pub fn typeset(
+ world: Tracked<dyn World>,
+ mut tracer: TrackedMut<Tracer>,
+ content: &Content,
+) -> SourceResult<Document> {
let library = world.library();
let styles = StyleChain::new(&library.styles);
@@ -27,6 +32,7 @@ pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Doc
let mut provider = StabilityProvider::new();
let mut vt = Vt {
world,
+ tracer: TrackedMut::reborrow_mut(&mut tracer),
provider: provider.track_mut(),
introspector: introspector.track_with(&constraint),
};
@@ -52,6 +58,8 @@ pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Doc
pub struct Vt<'a> {
/// The compilation environment.
pub world: Tracked<'a, dyn World>,
+ /// The tracer for inspection of the values an expression produces.
+ pub tracer: TrackedMut<'a, Tracer>,
/// Provides stable identities to nodes.
pub provider: TrackedMut<'a, StabilityProvider>,
/// Provides access to information about the document.
@@ -162,8 +170,8 @@ impl Introspector {
}
/// Query for all metadata matches for the given selector.
- pub fn query(&self, selector: Selector) -> Vec<&Content> {
- self.all().filter(|node| selector.matches(node)).collect()
+ pub fn query(&self, selector: Selector) -> Vec<Content> {
+ self.all().filter(|node| selector.matches(node)).cloned().collect()
}
/// Query for all metadata matches before the given id.
@@ -171,14 +179,15 @@ impl Introspector {
&self,
selector: Selector,
id: StableId,
- ) -> (Vec<&Content>, Vec<&Content>) {
+ ) -> (Vec<Content>, Vec<Content>) {
let mut iter = self.all();
let before = iter
.by_ref()
.take_while(|node| node.stable_id() != Some(id))
.filter(|node| selector.matches(node))
+ .cloned()
.collect();
- let after = iter.filter(|node| selector.matches(node)).collect();
+ let after = iter.filter(|node| selector.matches(node)).cloned().collect();
(before, after)
}