summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/diag.rs5
-rw-r--r--src/model/cast.rs7
-rw-r--r--src/model/content.rs35
-rw-r--r--src/model/eval.rs41
-rw-r--r--src/model/func.rs13
-rw-r--r--src/model/styles.rs34
6 files changed, 92 insertions, 43 deletions
diff --git a/src/diag.rs b/src/diag.rs
index 7631b6d3..613789f8 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -96,6 +96,8 @@ impl SourceError {
pub enum Tracepoint {
/// A function call.
Call(Option<EcoString>),
+ /// A show rule application.
+ Apply(EcoString),
/// A module import.
Import,
}
@@ -109,6 +111,9 @@ impl Display for Tracepoint {
Tracepoint::Call(None) => {
write!(f, "error occured in this function call")
}
+ Tracepoint::Apply(name) => {
+ write!(f, "error occured while applying show rule to this {name}")
+ }
Tracepoint::Import => {
write!(f, "error occured while importing this module")
}
diff --git a/src/model/cast.rs b/src/model/cast.rs
index d2e10a1f..d5b4893d 100644
--- a/src/model/cast.rs
+++ b/src/model/cast.rs
@@ -194,7 +194,12 @@ castable! {
Value::None => Self::Content(Content::empty()),
Value::Str(text) => Self::Content(item!(text)(text.into())),
Value::Content(content) => Self::Content(content),
- Value::Func(func) => Self::Func(func),
+ Value::Func(func) => {
+ if func.argc().map_or(false, |count| count != 1) {
+ Err("function must have exactly one parameter")?
+ }
+ Self::Func(func)
+ },
}
dynamic! {
diff --git a/src/model/content.rs b/src/model/content.rs
index 15ea33cb..210b8bde 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -11,6 +11,7 @@ use typst_macros::node;
use super::{Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Value, Vm};
use crate::diag::{SourceResult, StrResult};
+use crate::syntax::Span;
use crate::util::ReadableTypeId;
use crate::World;
@@ -20,7 +21,7 @@ use crate::World;
/// - anything written between square brackets in Typst
/// - any constructor function
#[derive(Clone, Hash)]
-pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>);
+pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>, Option<Span>);
impl Content {
/// Create empty content.
@@ -41,6 +42,16 @@ impl Content {
self.downcast::<SequenceNode>().map_or(false, |seq| seq.0.is_empty())
}
+ /// The node's span.
+ pub fn span(&self) -> Option<Span> {
+ self.2
+ }
+
+ /// The node's human-readable name.
+ pub fn name(&self) -> &'static str {
+ (*self.0).name()
+ }
+
/// The id of the contained node.
pub fn id(&self) -> NodeId {
(*self.0).id()
@@ -105,7 +116,7 @@ impl Content {
recipe: Recipe,
) -> SourceResult<Self> {
if recipe.selector.is_none() {
- recipe.transform.apply(world, recipe.span, || Value::Content(self))
+ recipe.transform.apply(world, recipe.span, self)
} else {
Ok(self.styled_with_entry(Style::Recipe(recipe)))
}
@@ -139,6 +150,21 @@ impl Content {
StyledNode { sub: self, map: styles }.pack()
}
+ /// Attach a span to the content.
+ pub fn spanned(mut self, span: Span) -> Self {
+ if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
+ styled.sub.2 = Some(span);
+ } else if let Some(styled) = self.downcast::<StyledNode>() {
+ self = StyledNode {
+ sub: styled.sub.clone().spanned(span),
+ map: styled.map.clone(),
+ }
+ .pack();
+ }
+ self.2 = Some(span);
+ self
+ }
+
/// Disable a show rule recipe.
pub fn guard(mut self, id: RecipeId) -> Self {
self.1.push(id);
@@ -252,7 +278,7 @@ pub trait Node: 'static {
where
Self: Debug + Hash + Sync + Send + Sized + 'static,
{
- Content(Arc::new(self), vec![])
+ Content(Arc::new(self), vec![], None)
}
/// Construct a node from the arguments.
@@ -277,6 +303,9 @@ pub trait Node: 'static {
/// A unique identifier of the node type.
fn id(&self) -> NodeId;
+ /// The node's name.
+ fn name(&self) -> &'static str;
+
/// Extract the pointer of the vtable of the trait object with the
/// given type `id` if this node implements that trait.
fn vtable(&self, id: TypeId) -> Option<*const ()>;
diff --git a/src/model/eval.rs b/src/model/eval.rs
index 2ed8c13b..8a596afb 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -155,29 +155,30 @@ impl Eval for ast::MarkupNode {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- match self {
- Self::Space(v) => Ok(match v.newlines() {
+ Ok(match self {
+ Self::Space(v) => match v.newlines() {
0..=1 => (vm.items.space)(),
_ => (vm.items.parbreak)(),
- }),
- Self::Linebreak(v) => v.eval(vm),
- Self::Text(v) => v.eval(vm),
- Self::Escape(v) => Ok((vm.items.text)(v.get().into())),
- Self::Shorthand(v) => v.eval(vm),
- Self::SmartQuote(v) => v.eval(vm),
- Self::Strong(v) => v.eval(vm),
- Self::Emph(v) => v.eval(vm),
- Self::Link(v) => v.eval(vm),
- Self::Raw(v) => v.eval(vm),
- Self::Math(v) => v.eval(vm),
- Self::Heading(v) => v.eval(vm),
- Self::List(v) => v.eval(vm),
- Self::Enum(v) => v.eval(vm),
- Self::Desc(v) => v.eval(vm),
- Self::Label(v) => v.eval(vm),
- Self::Ref(v) => v.eval(vm),
- Self::Expr(v) => v.eval(vm).map(|value| value.display(vm.world)),
+ },
+ Self::Linebreak(v) => v.eval(vm)?,
+ Self::Text(v) => v.eval(vm)?,
+ Self::Escape(v) => (vm.items.text)(v.get().into()),
+ Self::Shorthand(v) => v.eval(vm)?,
+ Self::SmartQuote(v) => v.eval(vm)?,
+ Self::Strong(v) => v.eval(vm)?,
+ Self::Emph(v) => v.eval(vm)?,
+ Self::Link(v) => v.eval(vm)?,
+ Self::Raw(v) => v.eval(vm)?,
+ Self::Math(v) => v.eval(vm)?,
+ Self::Heading(v) => v.eval(vm)?,
+ Self::List(v) => v.eval(vm)?,
+ Self::Enum(v) => v.eval(vm)?,
+ Self::Desc(v) => v.eval(vm)?,
+ Self::Label(v) => v.eval(vm)?,
+ Self::Ref(v) => v.eval(vm)?,
+ Self::Expr(v) => v.eval(vm)?.display(vm.world),
}
+ .spanned(self.span()))
}
}
diff --git a/src/model/func.rs b/src/model/func.rs
index 15434bbf..21e36784 100644
--- a/src/model/func.rs
+++ b/src/model/func.rs
@@ -68,9 +68,7 @@ impl Func {
/// The number of positional arguments this function takes, if known.
pub fn argc(&self) -> Option<usize> {
match self.0.as_ref() {
- Repr::Closure(closure) => Some(
- closure.params.iter().filter(|(_, default)| default.is_none()).count(),
- ),
+ Repr::Closure(closure) => closure.argc(),
Repr::With(wrapped, applied) => Some(wrapped.argc()?.saturating_sub(
applied.items.iter().filter(|arg| arg.name.is_none()).count(),
)),
@@ -239,6 +237,15 @@ impl Closure {
result
}
+
+ /// The number of positional arguments this function takes, if known.
+ pub fn argc(&self) -> Option<usize> {
+ if self.sink.is_some() {
+ return None;
+ }
+
+ Some(self.params.iter().filter(|(_, default)| default.is_none()).count())
+ }
}
/// A visitor that determines which variables to capture for a closure.
diff --git a/src/model/styles.rs b/src/model/styles.rs
index ced11e6a..a5b9aab3 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -8,7 +8,7 @@ use std::sync::Arc;
use comemo::{Prehashed, Tracked};
use super::{capability, Args, Content, Dict, Func, NodeId, Regex, Smart, Value};
-use crate::diag::SourceResult;
+use crate::diag::{SourceResult, Trace, Tracepoint};
use crate::geom::{
Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides,
};
@@ -382,9 +382,7 @@ impl Recipe {
return Ok(None);
}
- self.transform.apply(world, self.span, || {
- Value::Content(target.clone().guard(sel))
- })?
+ self.transform.apply(world, self.span, target.clone().guard(sel))?
}
Some(Selector::Regex(regex)) => {
@@ -402,9 +400,11 @@ impl Recipe {
result.push(make(text[cursor..start].into()));
}
- let transformed = self.transform.apply(world, self.span, || {
- Value::Content(make(mat.as_str().into()).guard(sel))
- })?;
+ let transformed = self.transform.apply(
+ world,
+ self.span,
+ make(mat.as_str().into()).guard(sel),
+ )?;
result.push(transformed);
cursor = mat.end();
@@ -486,20 +486,22 @@ pub enum Transform {
impl Transform {
/// Apply the transform.
- pub fn apply<F>(
+ pub fn apply(
&self,
world: Tracked<dyn World>,
- span: Span,
- arg: F,
- ) -> SourceResult<Content>
- where
- F: FnOnce() -> Value,
- {
+ rule_span: Span,
+ content: Content,
+ ) -> SourceResult<Content> {
match self {
Transform::Content(content) => Ok(content.clone()),
Transform::Func(func) => {
- let args = Args::new(span, [arg()]);
- Ok(func.call_detached(world, args)?.display(world))
+ let args = Args::new(rule_span, [Value::Content(content.clone())]);
+ let mut result = func.call_detached(world, args);
+ if let Some(span) = content.span() {
+ let point = || Tracepoint::Apply(content.name().into());
+ result = result.trace(world, point, span);
+ }
+ Ok(result?.display(world))
}
}
}