diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-17 16:27:40 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-17 18:18:47 +0100 |
| commit | af7fe4d76083c597ec2198a73383b9e3899d75ea (patch) | |
| tree | cb3b9766ee15dbfbecf7f9b9a2257859d7a7b3c7 /src | |
| parent | 6d64d3e8e9123f3fa8166c8b710e2b2c61ed5898 (diff) | |
Hover and autocomplete in show rules
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/array.rs | 14 | ||||
| -rw-r--r-- | src/eval/func.rs | 33 | ||||
| -rw-r--r-- | src/eval/mod.rs | 33 | ||||
| -rw-r--r-- | src/ide/analyze.rs | 20 | ||||
| -rw-r--r-- | src/lib.rs | 22 | ||||
| -rw-r--r-- | src/model/content.rs | 10 | ||||
| -rw-r--r-- | src/model/realize.rs | 8 | ||||
| -rw-r--r-- | src/model/styles.rs | 32 | ||||
| -rw-r--r-- | src/model/typeset.rs | 19 |
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() } @@ -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) } |
