mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Show it!
- New show rule syntax - Set if syntax - Removed wrap syntax
This commit is contained in:
parent
eb951c008b
commit
efd1853d06
@ -200,7 +200,7 @@ impl Cast<Spanned<Value>> for Marginal {
|
|||||||
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
fn cast(value: Spanned<Value>) -> StrResult<Self> {
|
||||||
match value.v {
|
match value.v {
|
||||||
Value::None => Ok(Self::None),
|
Value::None => Ok(Self::None),
|
||||||
Value::Str(v) => Ok(Self::Content(TextNode(v.into()).pack())),
|
Value::Str(v) => Ok(Self::Content(TextNode::packed(v))),
|
||||||
Value::Content(v) => Ok(Self::Content(v)),
|
Value::Content(v) => Ok(Self::Content(v)),
|
||||||
Value::Func(v) => Ok(Self::Func(v, value.span)),
|
Value::Func(v) => Ok(Self::Func(v, value.span)),
|
||||||
v => Err(format!(
|
v => Err(format!(
|
||||||
|
@ -18,7 +18,7 @@ pub struct MathNode {
|
|||||||
pub display: bool,
|
pub display: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node(Show, LayoutInline, Texify)]
|
#[node(Show, Finalize, LayoutInline, Texify)]
|
||||||
impl MathNode {
|
impl MathNode {
|
||||||
/// The math font family.
|
/// The math font family.
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
@ -29,6 +29,13 @@ impl MathNode {
|
|||||||
/// The spacing below display math.
|
/// The spacing below display math.
|
||||||
#[property(resolve, shorthand(around))]
|
#[property(resolve, shorthand(around))]
|
||||||
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
||||||
|
|
||||||
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
|
match name {
|
||||||
|
"display" => Some(Value::Bool(self.display)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Show for MathNode {
|
impl Show for MathNode {
|
||||||
@ -36,18 +43,16 @@ impl Show for MathNode {
|
|||||||
self.clone().pack()
|
self.clone().pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&self, _: &str) -> Option<Value> {
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
|
||||||
Ok(if self.display {
|
Ok(if self.display {
|
||||||
self.clone().pack().aligned(Axes::with_x(Some(Align::Center.into())))
|
self.clone().pack().aligned(Axes::with_x(Some(Align::Center.into())))
|
||||||
} else {
|
} else {
|
||||||
self.clone().pack()
|
self.clone().pack()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Finalize for MathNode {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: Tracked<dyn World>,
|
||||||
|
@ -9,8 +9,8 @@ pub use typst::frame::*;
|
|||||||
pub use typst::geom::*;
|
pub use typst::geom::*;
|
||||||
pub use typst::model::{
|
pub use typst::model::{
|
||||||
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
|
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
|
||||||
Content, Dict, Fold, Func, Key, Node, Resolve, Scope, Selector, Show, Smart, Str,
|
Content, Dict, Finalize, Fold, Func, Key, Node, Resolve, Scope, Selector, Show,
|
||||||
StyleChain, StyleMap, StyleVec, Value, Vm,
|
Smart, Str, StyleChain, StyleMap, StyleVec, Value, Vm,
|
||||||
};
|
};
|
||||||
pub use typst::syntax::{Span, Spanned};
|
pub use typst::syntax::{Span, Spanned};
|
||||||
pub use typst::util::{format_eco, EcoString};
|
pub use typst::util::{format_eco, EcoString};
|
||||||
|
@ -12,7 +12,7 @@ pub struct HeadingNode {
|
|||||||
pub body: Content,
|
pub body: Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node(Show)]
|
#[node(Show, Finalize)]
|
||||||
impl HeadingNode {
|
impl HeadingNode {
|
||||||
/// The heading's font family. Just the normal text family if `auto`.
|
/// The heading's font family. Just the normal text family if `auto`.
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
@ -67,12 +67,6 @@ impl HeadingNode {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for HeadingNode {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self { body: self.body.unguard(sel), ..*self }.pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -81,11 +75,19 @@ impl Show for HeadingNode {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
impl Show for HeadingNode {
|
||||||
Ok(BlockNode(self.body.clone()).pack())
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
|
Self { body: self.body.unguard(sel), ..*self }.pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
|
Ok(BlockNode(self.body.clone()).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Finalize for HeadingNode {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
|
@ -22,7 +22,7 @@ pub type EnumNode = ListNode<ENUM>;
|
|||||||
/// A description list.
|
/// A description list.
|
||||||
pub type DescNode = ListNode<DESC>;
|
pub type DescNode = ListNode<DESC>;
|
||||||
|
|
||||||
#[node(Show)]
|
#[node(Show, Finalize)]
|
||||||
impl<const L: ListKind> ListNode<L> {
|
impl<const L: ListKind> ListNode<L> {
|
||||||
/// How the list is labelled.
|
/// How the list is labelled.
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
@ -80,16 +80,6 @@ impl<const L: ListKind> ListNode<L> {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<const L: ListKind> Show for ListNode<L> {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self {
|
|
||||||
items: self.items.map(|item| item.unguard(sel)),
|
|
||||||
..*self
|
|
||||||
}
|
|
||||||
.pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -101,8 +91,18 @@ impl<const L: ListKind> Show for ListNode<L> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(
|
impl<const L: ListKind> Show for ListNode<L> {
|
||||||
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
|
Self {
|
||||||
|
items: self.items.map(|item| item.unguard(sel)),
|
||||||
|
..*self
|
||||||
|
}
|
||||||
|
.pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
@ -140,7 +140,7 @@ impl<const L: ListKind> Show for ListNode<L> {
|
|||||||
ListItem::Enum(_, body) => body.as_ref().clone(),
|
ListItem::Enum(_, body) => body.as_ref().clone(),
|
||||||
ListItem::Desc(item) => Content::sequence(vec![
|
ListItem::Desc(item) => Content::sequence(vec![
|
||||||
HNode { amount: (-body_indent).into(), weak: false }.pack(),
|
HNode { amount: (-body_indent).into(), weak: false }.pack(),
|
||||||
(item.term.clone() + TextNode(':'.into()).pack()).strong(),
|
(item.term.clone() + TextNode::packed(':')).strong(),
|
||||||
SpaceNode.pack(),
|
SpaceNode.pack(),
|
||||||
item.body.clone(),
|
item.body.clone(),
|
||||||
]),
|
]),
|
||||||
@ -162,7 +162,9 @@ impl<const L: ListKind> Show for ListNode<L> {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const L: ListKind> Finalize for ListNode<L> {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: Tracked<dyn World>,
|
||||||
@ -312,14 +314,14 @@ impl Label {
|
|||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Default => match kind {
|
Self::Default => match kind {
|
||||||
LIST => TextNode('•'.into()).pack(),
|
LIST => TextNode::packed('•'),
|
||||||
ENUM => TextNode(format_eco!("{}.", number)).pack(),
|
ENUM => TextNode::packed(format_eco!("{}.", number)),
|
||||||
DESC | _ => panic!("description lists don't have a label"),
|
DESC | _ => panic!("description lists don't have a label"),
|
||||||
},
|
},
|
||||||
Self::Pattern(prefix, numbering, upper, suffix) => {
|
Self::Pattern(prefix, numbering, upper, suffix) => {
|
||||||
let fmt = numbering.apply(number);
|
let fmt = numbering.apply(number);
|
||||||
let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() };
|
let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() };
|
||||||
TextNode(format_eco!("{}{}{}", prefix, mid, suffix)).pack()
|
TextNode::packed(format_eco!("{}{}{}", prefix, mid, suffix))
|
||||||
}
|
}
|
||||||
Self::Content(content) => content.clone(),
|
Self::Content(content) => content.clone(),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
|
@ -8,7 +8,14 @@ pub struct RefNode(pub EcoString);
|
|||||||
#[node(Show)]
|
#[node(Show)]
|
||||||
impl RefNode {
|
impl RefNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.expect("label")?).pack())
|
Ok(Self(args.expect("target")?).pack())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
|
match name {
|
||||||
|
"target" => Some(Value::Str(self.0.clone().into())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,14 +24,7 @@ impl Show for RefNode {
|
|||||||
Self(self.0.clone()).pack()
|
Self(self.0.clone()).pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
match name {
|
Ok(TextNode::packed(format_eco!("@{}", self.0)))
|
||||||
"label" => Some(Value::Str(self.0.clone().into())),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
|
||||||
Ok(TextNode(format_eco!("@{}", self.0)).pack())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ pub struct TableNode {
|
|||||||
pub cells: Vec<Content>,
|
pub cells: Vec<Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node(Show)]
|
#[node(Show, Finalize)]
|
||||||
impl TableNode {
|
impl TableNode {
|
||||||
/// How to fill the cells.
|
/// How to fill the cells.
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
@ -46,6 +46,15 @@ impl TableNode {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
|
match name {
|
||||||
|
"cells" => Some(Value::Array(
|
||||||
|
self.cells.iter().cloned().map(Value::Content).collect(),
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Show for TableNode {
|
impl Show for TableNode {
|
||||||
@ -58,16 +67,7 @@ impl Show for TableNode {
|
|||||||
.pack()
|
.pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn show(
|
||||||
match name {
|
|
||||||
"cells" => Some(Value::Array(
|
|
||||||
self.cells.iter().cloned().map(Value::Content).collect(),
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn realize(
|
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
@ -106,7 +106,9 @@ impl Show for TableNode {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Finalize for TableNode {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: Tracked<dyn World>,
|
||||||
|
@ -37,12 +37,6 @@ impl<const L: DecoLine> DecoNode<L> {
|
|||||||
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.expect("body")?).pack())
|
Ok(Self(args.expect("body")?).pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<const L: DecoLine> Show for DecoNode<L> {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self(self.0.unguard(sel)).pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -50,12 +44,14 @@ impl<const L: DecoLine> Show for DecoNode<L> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(
|
impl<const L: DecoLine> Show for DecoNode<L> {
|
||||||
&self,
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
_: Tracked<dyn World>,
|
Self(self.0.unguard(sel)).pack()
|
||||||
styles: StyleChain,
|
}
|
||||||
) -> SourceResult<Content> {
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(
|
Ok(self.0.clone().styled(
|
||||||
TextNode::DECO,
|
TextNode::DECO,
|
||||||
Decoration {
|
Decoration {
|
||||||
@ -81,6 +77,15 @@ pub(super) struct Decoration {
|
|||||||
pub evade: bool,
|
pub evade: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Fold for Decoration {
|
||||||
|
type Output = Vec<Self>;
|
||||||
|
|
||||||
|
fn fold(self, mut outer: Self::Output) -> Self::Output {
|
||||||
|
outer.insert(0, self);
|
||||||
|
outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A kind of decorative line.
|
/// A kind of decorative line.
|
||||||
pub type DecoLine = usize;
|
pub type DecoLine = usize;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ impl LinkNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node(Show)]
|
#[node(Show, Finalize)]
|
||||||
impl LinkNode {
|
impl LinkNode {
|
||||||
/// The fill color of text in the link. Just the surrounding text color
|
/// The fill color of text in the link. Just the surrounding text color
|
||||||
/// if `auto`.
|
/// if `auto`.
|
||||||
@ -33,16 +33,6 @@ impl LinkNode {
|
|||||||
};
|
};
|
||||||
Ok(Self { dest, body }.pack())
|
Ok(Self { dest, body }.pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for LinkNode {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self {
|
|
||||||
dest: self.dest.clone(),
|
|
||||||
body: self.body.as_ref().map(|body| body.unguard(sel)),
|
|
||||||
}
|
|
||||||
.pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -57,25 +47,33 @@ impl Show for LinkNode {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
impl Show for LinkNode {
|
||||||
Ok(self
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
.body
|
Self {
|
||||||
.clone()
|
dest: self.dest.clone(),
|
||||||
.unwrap_or_else(|| match &self.dest {
|
body: self.body.as_ref().map(|body| body.unguard(sel)),
|
||||||
Destination::Url(url) => {
|
}
|
||||||
let mut text = url.as_str();
|
.pack()
|
||||||
for prefix in ["mailto:", "tel:"] {
|
|
||||||
text = text.trim_start_matches(prefix);
|
|
||||||
}
|
|
||||||
let shorter = text.len() < url.len();
|
|
||||||
TextNode(if shorter { text.into() } else { url.clone() }).pack()
|
|
||||||
}
|
|
||||||
Destination::Internal(_) => Content::empty(),
|
|
||||||
})
|
|
||||||
.styled(TextNode::LINK, Some(self.dest.clone())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
|
Ok(self.body.clone().unwrap_or_else(|| match &self.dest {
|
||||||
|
Destination::Url(url) => {
|
||||||
|
let mut text = url.as_str();
|
||||||
|
for prefix in ["mailto:", "tel:"] {
|
||||||
|
text = text.trim_start_matches(prefix);
|
||||||
|
}
|
||||||
|
let shorter = text.len() < url.len();
|
||||||
|
TextNode::packed(if shorter { text.into() } else { url.clone() })
|
||||||
|
}
|
||||||
|
Destination::Internal(_) => Content::empty(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Finalize for LinkNode {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: Tracked<dyn World>,
|
||||||
@ -83,6 +81,8 @@ impl Show for LinkNode {
|
|||||||
mut realized: Content,
|
mut realized: Content,
|
||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
|
map.set(TextNode::LINK, Some(self.dest.clone()));
|
||||||
|
|
||||||
if let Smart::Custom(fill) = styles.get(Self::FILL) {
|
if let Smart::Custom(fill) = styles.get(Self::FILL) {
|
||||||
map.set(TextNode::FILL, fill);
|
map.set(TextNode::FILL, fill);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,13 @@ use crate::prelude::*;
|
|||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct TextNode(pub EcoString);
|
pub struct TextNode(pub EcoString);
|
||||||
|
|
||||||
|
impl TextNode {
|
||||||
|
/// Create a new packed text node.
|
||||||
|
pub fn packed(text: impl Into<EcoString>) -> Content {
|
||||||
|
Self(text.into()).pack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl TextNode {
|
impl TextNode {
|
||||||
/// A prioritized sequence of font families.
|
/// A prioritized sequence of font families.
|
||||||
@ -486,12 +493,6 @@ impl StrongNode {
|
|||||||
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.expect("body")?).pack())
|
Ok(Self(args.expect("body")?).pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for StrongNode {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self(self.0.unguard(sel)).pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -499,8 +500,14 @@ impl Show for StrongNode {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
impl Show for StrongNode {
|
||||||
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
|
Self(self.0.unguard(sel)).pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
|
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -514,12 +521,6 @@ impl EmphNode {
|
|||||||
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.expect("body")?).pack())
|
Ok(Self(args.expect("body")?).pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for EmphNode {
|
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content {
|
|
||||||
Self(self.0.unguard(sel)).pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -527,8 +528,14 @@ impl Show for EmphNode {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
impl Show for EmphNode {
|
||||||
|
fn unguard_parts(&self, sel: Selector) -> Content {
|
||||||
|
Self(self.0.unguard(sel)).pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
|
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,12 +551,3 @@ impl Fold for Toggle {
|
|||||||
!outer
|
!outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Decoration {
|
|
||||||
type Output = Vec<Self>;
|
|
||||||
|
|
||||||
fn fold(self, mut outer: Self::Output) -> Self::Output {
|
|
||||||
outer.insert(0, self);
|
|
||||||
outer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,7 @@ pub struct RawNode {
|
|||||||
pub block: bool,
|
pub block: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node(Show)]
|
#[node(Show, Finalize)]
|
||||||
impl RawNode {
|
impl RawNode {
|
||||||
/// The language to syntax-highlight in.
|
/// The language to syntax-highlight in.
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
@ -41,12 +41,6 @@ impl RawNode {
|
|||||||
}
|
}
|
||||||
.pack())
|
.pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for RawNode {
|
|
||||||
fn unguard_parts(&self, _: Selector) -> Content {
|
|
||||||
Self { text: self.text.clone(), ..*self }.pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -55,12 +49,14 @@ impl Show for RawNode {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(
|
impl Show for RawNode {
|
||||||
&self,
|
fn unguard_parts(&self, _: Selector) -> Content {
|
||||||
_: Tracked<dyn World>,
|
Self { text: self.text.clone(), ..*self }.pack()
|
||||||
styles: StyleChain,
|
}
|
||||||
) -> SourceResult<Content> {
|
|
||||||
|
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
||||||
let foreground = THEME
|
let foreground = THEME
|
||||||
.settings
|
.settings
|
||||||
@ -100,7 +96,7 @@ impl Show for RawNode {
|
|||||||
|
|
||||||
Content::sequence(seq)
|
Content::sequence(seq)
|
||||||
} else {
|
} else {
|
||||||
TextNode(self.text.clone()).pack()
|
TextNode::packed(self.text.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.block {
|
if self.block {
|
||||||
@ -114,7 +110,9 @@ impl Show for RawNode {
|
|||||||
|
|
||||||
Ok(realized.styled_with_map(map))
|
Ok(realized.styled_with_map(map))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Finalize for RawNode {
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: Tracked<dyn World>,
|
||||||
@ -134,7 +132,7 @@ impl Show for RawNode {
|
|||||||
|
|
||||||
/// Style a piece of text with a syntect style.
|
/// Style a piece of text with a syntect style.
|
||||||
fn styled(piece: &str, foreground: Paint, style: Style) -> Content {
|
fn styled(piece: &str, foreground: Paint, style: Style) -> Content {
|
||||||
let mut body = TextNode(piece.into()).pack();
|
let mut body = TextNode::packed(piece);
|
||||||
|
|
||||||
let paint = style.foreground.into();
|
let paint = style.foreground.into();
|
||||||
if paint != foreground {
|
if paint != foreground {
|
||||||
|
@ -33,12 +33,6 @@ impl<const S: ShiftKind> ShiftNode<S> {
|
|||||||
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.expect("body")?).pack())
|
Ok(Self(args.expect("body")?).pack())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<const S: ShiftKind> Show for ShiftNode<S> {
|
|
||||||
fn unguard_parts(&self, _: Selector) -> Content {
|
|
||||||
Self(self.0.clone()).pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
@ -46,8 +40,14 @@ impl<const S: ShiftKind> Show for ShiftNode<S> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn realize(
|
impl<const S: ShiftKind> Show for ShiftNode<S> {
|
||||||
|
fn unguard_parts(&self, _: Selector) -> Content {
|
||||||
|
Self(self.0.clone()).pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
@ -56,7 +56,7 @@ impl<const S: ShiftKind> Show for ShiftNode<S> {
|
|||||||
if styles.get(Self::TYPOGRAPHIC) {
|
if styles.get(Self::TYPOGRAPHIC) {
|
||||||
if let Some(text) = search_text(&self.0, S) {
|
if let Some(text) = search_text(&self.0, S) {
|
||||||
if is_shapable(world, &text, styles) {
|
if is_shapable(world, &text, styles) {
|
||||||
transformed = Some(TextNode(text).pack());
|
transformed = Some(TextNode::packed(text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -47,6 +47,7 @@ fn expand_node(
|
|||||||
let mut properties = vec![];
|
let mut properties = vec![];
|
||||||
let mut construct = None;
|
let mut construct = None;
|
||||||
let mut set = None;
|
let mut set = None;
|
||||||
|
let mut field = None;
|
||||||
|
|
||||||
for item in std::mem::take(&mut impl_block.items) {
|
for item in std::mem::take(&mut impl_block.items) {
|
||||||
match item {
|
match item {
|
||||||
@ -61,6 +62,7 @@ fn expand_node(
|
|||||||
match method.sig.ident.to_string().as_str() {
|
match method.sig.ident.to_string().as_str() {
|
||||||
"construct" => construct = Some(method),
|
"construct" => construct = Some(method),
|
||||||
"set" => set = Some(method),
|
"set" => set = Some(method),
|
||||||
|
"field" => field = Some(method),
|
||||||
_ => return Err(Error::new(method.span(), "unexpected method")),
|
_ => return Err(Error::new(method.span(), "unexpected method")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,6 +83,14 @@ fn expand_node(
|
|||||||
|
|
||||||
let set = generate_set(&properties, set);
|
let set = generate_set(&properties, set);
|
||||||
|
|
||||||
|
let field = field.unwrap_or_else(|| {
|
||||||
|
parse_quote! {
|
||||||
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let items: syn::punctuated::Punctuated<Ident, syn::Token![,]> =
|
let items: syn::punctuated::Punctuated<Ident, syn::Token![,]> =
|
||||||
parse_quote! { #stream };
|
parse_quote! { #stream };
|
||||||
|
|
||||||
@ -115,11 +125,13 @@ fn expand_node(
|
|||||||
impl<#params> model::Node for #self_ty {
|
impl<#params> model::Node for #self_ty {
|
||||||
#construct
|
#construct
|
||||||
#set
|
#set
|
||||||
#vtable
|
#field
|
||||||
|
|
||||||
fn id(&self) -> model::NodeId {
|
fn id(&self) -> model::NodeId {
|
||||||
model::NodeId::of::<Self>()
|
model::NodeId::of::<Self>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#vtable
|
||||||
}
|
}
|
||||||
|
|
||||||
#(#key_modules)*
|
#(#key_modules)*
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::{Pattern, Regex, Value};
|
use super::{Content, Pattern, Regex, Transform, Value};
|
||||||
use crate::diag::{with_alternative, StrResult};
|
use crate::diag::{with_alternative, StrResult};
|
||||||
use crate::font::{FontStretch, FontStyle, FontWeight};
|
use crate::font::{FontStretch, FontStyle, FontWeight};
|
||||||
use crate::frame::{Destination, Lang, Location, Region};
|
use crate::frame::{Destination, Lang, Location, Region};
|
||||||
@ -189,6 +189,15 @@ castable! {
|
|||||||
@regex: Regex => Self::Regex(regex.clone()),
|
@regex: Regex => Self::Regex(regex.clone()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
castable! {
|
||||||
|
Transform,
|
||||||
|
Expected: "content or function",
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
dynamic! {
|
dynamic! {
|
||||||
Dir: "direction",
|
Dir: "direction",
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,14 @@ use std::iter::{self, Sum};
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use comemo::Tracked;
|
||||||
use siphasher::sip128::{Hasher128, SipHasher};
|
use siphasher::sip128::{Hasher128, SipHasher};
|
||||||
use typst_macros::node;
|
use typst_macros::node;
|
||||||
|
|
||||||
use super::{Args, Key, Property, Selector, StyleEntry, StyleMap, Vm};
|
use super::{Args, Key, Property, Recipe, Selector, StyleEntry, StyleMap, Value, Vm};
|
||||||
use crate as typst;
|
|
||||||
use crate::diag::{SourceResult, StrResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::util::{EcoString, ReadableTypeId};
|
use crate::util::ReadableTypeId;
|
||||||
|
use crate::World;
|
||||||
|
|
||||||
/// Composable representation of styled content.
|
/// Composable representation of styled content.
|
||||||
///
|
///
|
||||||
@ -27,11 +28,6 @@ impl Content {
|
|||||||
SequenceNode(vec![]).pack()
|
SequenceNode(vec![]).pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create content from a string of text.
|
|
||||||
pub fn text(text: impl Into<EcoString>) -> Self {
|
|
||||||
item!(text)(text.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new sequence node from multiples nodes.
|
/// Create a new sequence node from multiples nodes.
|
||||||
pub fn sequence(seq: Vec<Self>) -> Self {
|
pub fn sequence(seq: Vec<Self>) -> Self {
|
||||||
match seq.as_slice() {
|
match seq.as_slice() {
|
||||||
@ -65,6 +61,11 @@ impl Content {
|
|||||||
Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::<T>()
|
Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access a field on this content.
|
||||||
|
pub fn field(&self, name: &str) -> Option<Value> {
|
||||||
|
self.0.field(name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether this content has the given capability.
|
/// Whether this content has the given capability.
|
||||||
pub fn has<C>(&self) -> bool
|
pub fn has<C>(&self) -> bool
|
||||||
where
|
where
|
||||||
@ -97,6 +98,19 @@ impl Content {
|
|||||||
self.styled_with_entry(StyleEntry::Property(Property::new(key, value)))
|
self.styled_with_entry(StyleEntry::Property(Property::new(key, value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
||||||
|
if recipe.pattern.is_none() {
|
||||||
|
recipe.transform.apply(world, recipe.span, || Value::Content(self))
|
||||||
|
} else {
|
||||||
|
Ok(self.styled_with_entry(StyleEntry::Recipe(recipe)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Style this content with a style entry.
|
/// Style this content with a style entry.
|
||||||
pub fn styled_with_entry(mut self, entry: StyleEntry) -> Self {
|
pub fn styled_with_entry(mut self, entry: StyleEntry) -> Self {
|
||||||
if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
|
if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
|
||||||
@ -242,6 +256,9 @@ pub trait Node: 'static {
|
|||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
/// Access a field on this node.
|
||||||
|
fn field(&self, name: &str) -> Option<Value>;
|
||||||
|
|
||||||
/// A unique identifier of the node type.
|
/// A unique identifier of the node type.
|
||||||
fn id(&self) -> NodeId;
|
fn id(&self) -> NodeId;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func,
|
methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func,
|
||||||
Pattern, Recipe, Scope, Scopes, Show, StyleEntry, StyleMap, Value, Vm,
|
Pattern, Recipe, Scope, Scopes, StyleMap, Transform, Value, Vm,
|
||||||
};
|
};
|
||||||
use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint};
|
use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint};
|
||||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
||||||
@ -133,12 +133,8 @@ fn eval_markup(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_markup(vm, nodes)?.styled_with_entry(StyleEntry::Recipe(recipe))
|
|
||||||
}
|
|
||||||
ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => {
|
|
||||||
let tail = eval_markup(vm, nodes)?;
|
let tail = eval_markup(vm, nodes)?;
|
||||||
vm.scopes.top.define(wrap.binding().take(), tail);
|
tail.styled_with_recipe(vm.world, recipe)?
|
||||||
wrap.body().eval(vm)?.display(vm.world)
|
|
||||||
}
|
}
|
||||||
_ => node.eval(vm)?,
|
_ => node.eval(vm)?,
|
||||||
});
|
});
|
||||||
@ -408,7 +404,6 @@ impl Eval for ast::Expr {
|
|||||||
Self::Let(v) => v.eval(vm),
|
Self::Let(v) => v.eval(vm),
|
||||||
Self::Set(_) => bail!(forbidden("set")),
|
Self::Set(_) => bail!(forbidden("set")),
|
||||||
Self::Show(_) => bail!(forbidden("show")),
|
Self::Show(_) => bail!(forbidden("show")),
|
||||||
Self::Wrap(_) => bail!(forbidden("wrap")),
|
|
||||||
Self::Conditional(v) => v.eval(vm),
|
Self::Conditional(v) => v.eval(vm),
|
||||||
Self::While(v) => v.eval(vm),
|
Self::While(v) => v.eval(vm),
|
||||||
Self::For(v) => v.eval(vm),
|
Self::For(v) => v.eval(vm),
|
||||||
@ -484,18 +479,12 @@ fn eval_code(
|
|||||||
}
|
}
|
||||||
ast::Expr::Show(show) => {
|
ast::Expr::Show(show) => {
|
||||||
let recipe = show.eval(vm)?;
|
let recipe = show.eval(vm)?;
|
||||||
let entry = StyleEntry::Recipe(recipe);
|
|
||||||
if vm.flow.is_some() {
|
if vm.flow.is_some() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tail = eval_code(vm, exprs)?.display(vm.world);
|
let tail = eval_code(vm, exprs)?.display(vm.world);
|
||||||
Value::Content(tail.styled_with_entry(entry))
|
Value::Content(tail.styled_with_recipe(vm.world, recipe)?)
|
||||||
}
|
|
||||||
ast::Expr::Wrap(wrap) => {
|
|
||||||
let tail = eval_code(vm, exprs)?;
|
|
||||||
vm.scopes.top.define(wrap.binding().take(), tail);
|
|
||||||
wrap.body().eval(vm)?
|
|
||||||
}
|
}
|
||||||
_ => expr.eval(vm)?,
|
_ => expr.eval(vm)?,
|
||||||
};
|
};
|
||||||
@ -671,9 +660,8 @@ impl Eval for ast::FieldAccess {
|
|||||||
|
|
||||||
Ok(match object {
|
Ok(match object {
|
||||||
Value::Dict(dict) => dict.get(&field).at(span)?.clone(),
|
Value::Dict(dict) => dict.get(&field).at(span)?.clone(),
|
||||||
Value::Content(node) => node
|
Value::Content(content) => content
|
||||||
.to::<dyn Show>()
|
.field(&field)
|
||||||
.and_then(|node| node.field(&field))
|
|
||||||
.ok_or_else(|| format!("unknown field {field:?}"))
|
.ok_or_else(|| format!("unknown field {field:?}"))
|
||||||
.at(span)?,
|
.at(span)?,
|
||||||
v => bail!(self.target().span(), "cannot access field on {}", v.type_name()),
|
v => bail!(self.target().span(), "cannot access field on {}", v.type_name()),
|
||||||
@ -838,6 +826,11 @@ impl Eval for ast::SetRule {
|
|||||||
type Output = StyleMap;
|
type Output = StyleMap;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
if let Some(condition) = self.condition() {
|
||||||
|
if !condition.eval(vm)?.cast::<bool>().at(condition.span())? {
|
||||||
|
return Ok(StyleMap::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
let target = self.target();
|
let target = self.target();
|
||||||
let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
|
let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
@ -849,36 +842,16 @@ impl Eval for ast::ShowRule {
|
|||||||
type Output = Recipe;
|
type Output = Recipe;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
// Evaluate the target function.
|
let pattern = self
|
||||||
let pattern = self.pattern();
|
.pattern()
|
||||||
let pattern = pattern.eval(vm)?.cast::<Pattern>().at(pattern.span())?;
|
.map(|pattern| pattern.eval(vm)?.cast::<Pattern>().at(pattern.span()))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
// Collect captured variables.
|
let transform = self.transform();
|
||||||
let captured = {
|
let span = transform.span();
|
||||||
let mut visitor = CapturesVisitor::new(&vm.scopes);
|
let transform = transform.eval(vm)?.cast::<Transform>().at(span)?;
|
||||||
visitor.visit(self.as_untyped());
|
|
||||||
visitor.finish()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define parameters.
|
Ok(Recipe { span, pattern, transform })
|
||||||
let mut params = vec![];
|
|
||||||
if let Some(binding) = self.binding() {
|
|
||||||
params.push((binding.take(), None));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the recipe function.
|
|
||||||
let body = self.body();
|
|
||||||
let span = body.span();
|
|
||||||
let func = Func::from_closure(Closure {
|
|
||||||
location: vm.location,
|
|
||||||
name: None,
|
|
||||||
captured,
|
|
||||||
params,
|
|
||||||
sink: None,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Recipe { pattern, func: Spanned::new(func, span) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1066,7 +1039,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
|
|||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::BreakStmt {
|
impl Eval for ast::LoopBreak {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1077,7 +1050,7 @@ impl Eval for ast::BreakStmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::ContinueStmt {
|
impl Eval for ast::LoopContinue {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1088,7 +1061,7 @@ impl Eval for ast::ContinueStmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::ReturnStmt {
|
impl Eval for ast::FuncReturn {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
@ -310,16 +310,6 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
self.bind(expr.binding());
|
self.bind(expr.binding());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A show rule contains a binding, but that binding is only active
|
|
||||||
// after the target has been evaluated.
|
|
||||||
Some(ast::Expr::Show(show)) => {
|
|
||||||
self.visit(show.pattern().as_untyped());
|
|
||||||
if let Some(binding) = show.binding() {
|
|
||||||
self.bind(binding);
|
|
||||||
}
|
|
||||||
self.visit(show.body().as_untyped());
|
|
||||||
}
|
|
||||||
|
|
||||||
// A for loop contains one or two bindings in its pattern. These are
|
// A for loop contains one or two bindings in its pattern. These are
|
||||||
// active after the iterable is evaluated but before the body is
|
// active after the iterable is evaluated but before the body is
|
||||||
// evaluated.
|
// evaluated.
|
||||||
@ -391,9 +381,9 @@ mod tests {
|
|||||||
test("{(x, y: x + z) => x + y}", &["x", "z"]);
|
test("{(x, y: x + z) => x + y}", &["x", "z"]);
|
||||||
|
|
||||||
// Show rule.
|
// Show rule.
|
||||||
test("#show x: y as x", &["y"]);
|
test("#show y: x => x", &["y"]);
|
||||||
test("#show x: y as x + z", &["y", "z"]);
|
test("#show y: x => x + z", &["y", "z"]);
|
||||||
test("#show x: x as x", &["x"]);
|
test("#show x: x => x", &["x"]);
|
||||||
|
|
||||||
// For loop.
|
// For loop.
|
||||||
test("#for x in y { x + z }", &["y", "z"]);
|
test("#for x in y { x + z }", &["y", "z"]);
|
||||||
|
@ -48,11 +48,11 @@ pub struct LangItems {
|
|||||||
pub em: fn(StyleChain) -> Abs,
|
pub em: fn(StyleChain) -> Abs,
|
||||||
/// Access the text direction.
|
/// Access the text direction.
|
||||||
pub dir: fn(StyleChain) -> Dir,
|
pub dir: fn(StyleChain) -> Dir,
|
||||||
/// A space.
|
/// Whitespace.
|
||||||
pub space: fn() -> Content,
|
pub space: fn() -> Content,
|
||||||
/// A forced line break.
|
/// A forced line break: `\`.
|
||||||
pub linebreak: fn(justify: bool) -> Content,
|
pub linebreak: fn(justify: bool) -> Content,
|
||||||
/// Plain text.
|
/// Plain text without markup.
|
||||||
pub text: fn(text: EcoString) -> Content,
|
pub text: fn(text: EcoString) -> Content,
|
||||||
/// A smart quote: `'` or `"`.
|
/// A smart quote: `'` or `"`.
|
||||||
pub smart_quote: fn(double: bool) -> Content,
|
pub smart_quote: fn(double: bool) -> Content,
|
||||||
@ -72,18 +72,18 @@ pub struct LangItems {
|
|||||||
pub heading: fn(level: NonZeroUsize, body: Content) -> Content,
|
pub heading: fn(level: NonZeroUsize, body: Content) -> Content,
|
||||||
/// An item in an unordered list: `- ...`.
|
/// An item in an unordered list: `- ...`.
|
||||||
pub list_item: fn(body: Content) -> Content,
|
pub list_item: fn(body: Content) -> Content,
|
||||||
/// An item in an enumeration (ordered list): `1. ...`.
|
/// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
|
||||||
pub enum_item: fn(number: Option<usize>, body: Content) -> Content,
|
pub enum_item: fn(number: Option<usize>, body: Content) -> Content,
|
||||||
/// An item in a description list: `/ Term: Details`.
|
/// An item in a description list: `/ Term: Details`.
|
||||||
pub desc_item: fn(term: Content, body: Content) -> Content,
|
pub desc_item: fn(term: Content, body: Content) -> Content,
|
||||||
/// A math formula: `$x$`, `$ x^2 $`.
|
/// A mathematical formula: `$x$`, `$ x^2 $`.
|
||||||
pub math: fn(children: Vec<Content>, display: bool) -> Content,
|
pub math: fn(children: Vec<Content>, display: bool) -> Content,
|
||||||
/// A atom in a formula: `x`, `+`, `12`.
|
/// An atom in a formula: `x`, `+`, `12`.
|
||||||
pub math_atom: fn(atom: EcoString) -> Content,
|
pub math_atom: fn(atom: EcoString) -> Content,
|
||||||
/// A base with an optional sub- and superscript in a formula: `a_1^2`.
|
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
|
||||||
pub math_script:
|
pub math_script:
|
||||||
fn(base: Content, sub: Option<Content>, sup: Option<Content>) -> Content,
|
fn(base: Content, sub: Option<Content>, sup: Option<Content>) -> Content,
|
||||||
/// A fraction in a formula: `x/2`
|
/// A fraction in a formula: `x/2`.
|
||||||
pub math_frac: fn(num: Content, denom: Content) -> Content,
|
pub math_frac: fn(num: Content, denom: Content) -> Content,
|
||||||
/// An alignment indicator in a formula: `&`, `&&`.
|
/// An alignment indicator in a formula: `&`, `&&`.
|
||||||
pub math_align: fn(count: usize) -> Content,
|
pub math_align: fn(count: usize) -> Content,
|
||||||
|
@ -19,8 +19,8 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
(a, None) => a,
|
(a, None) => a,
|
||||||
(None, b) => b,
|
(None, b) => b,
|
||||||
(Str(a), Str(b)) => Str(a + b),
|
(Str(a), Str(b)) => Str(a + b),
|
||||||
(Str(a), Content(b)) => Content(super::Content::text(a) + b),
|
(Str(a), Content(b)) => Content(item!(text)(a.into()) + b),
|
||||||
(Content(a), Str(b)) => Content(a + super::Content::text(b)),
|
(Content(a), Str(b)) => Content(a + item!(text)(b.into())),
|
||||||
(Content(a), Content(b)) => Content(a + b),
|
(Content(a), Content(b)) => Content(a + b),
|
||||||
(Array(a), Array(b)) => Array(a + b),
|
(Array(a), Array(b)) => Array(a + b),
|
||||||
(Dict(a), Dict(b)) => Dict(a + b),
|
(Dict(a), Dict(b)) => Dict(a + b),
|
||||||
@ -85,8 +85,8 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
|
|
||||||
(Str(a), Str(b)) => Str(a + b),
|
(Str(a), Str(b)) => Str(a + b),
|
||||||
(Content(a), Content(b)) => Content(a + b),
|
(Content(a), Content(b)) => Content(a + b),
|
||||||
(Content(a), Str(b)) => Content(a + super::Content::text(b)),
|
(Content(a), Str(b)) => Content(a + item!(text)(b.into())),
|
||||||
(Str(a), Content(b)) => Content(super::Content::text(a) + b),
|
(Str(a), Content(b)) => Content(item!(text)(a.into()) + b),
|
||||||
|
|
||||||
(Array(a), Array(b)) => Array(a + b),
|
(Array(a), Array(b)) => Array(a + b),
|
||||||
(Dict(a), Dict(b)) => Dict(a + b),
|
(Dict(a), Dict(b)) => Dict(a + b),
|
||||||
|
@ -12,7 +12,7 @@ use crate::diag::SourceResult;
|
|||||||
use crate::geom::{
|
use crate::geom::{
|
||||||
Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides,
|
Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides,
|
||||||
};
|
};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Span;
|
||||||
use crate::util::ReadableTypeId;
|
use crate::util::ReadableTypeId;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -283,17 +283,17 @@ impl<'a> StyleChain<'a> {
|
|||||||
.unguard_parts(sel)
|
.unguard_parts(sel)
|
||||||
.to::<dyn Show>()
|
.to::<dyn Show>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.realize(world, self)?;
|
.show(world, self)?;
|
||||||
realized = Some(content.styled_with_entry(StyleEntry::Guard(sel)));
|
realized = Some(content.styled_with_entry(StyleEntry::Guard(sel)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize only if guarding didn't stop any recipe.
|
// Finalize only if guarding didn't stop any recipe.
|
||||||
if !guarded {
|
if !guarded {
|
||||||
if let Some(content) = realized {
|
if let Some(node) = node.to::<dyn Finalize>() {
|
||||||
realized = Some(
|
if let Some(content) = realized {
|
||||||
node.to::<dyn Show>().unwrap().finalize(world, self, content)?,
|
realized = Some(node.finalize(world, self, content)?);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -974,18 +974,20 @@ impl Fold for PartialStroke<Abs> {
|
|||||||
/// A show rule recipe.
|
/// A show rule recipe.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
/// The patterns to customize.
|
/// The span errors are reported with.
|
||||||
pub pattern: Pattern,
|
pub span: Span,
|
||||||
/// The function that defines the recipe.
|
/// The pattern that the rule applies to.
|
||||||
pub func: Spanned<Func>,
|
pub pattern: Option<Pattern>,
|
||||||
|
/// The transformation to perform on the match.
|
||||||
|
pub transform: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Recipe {
|
impl Recipe {
|
||||||
/// Whether the recipe is applicable to the target.
|
/// Whether the recipe is applicable to the target.
|
||||||
pub fn applicable(&self, target: Target) -> bool {
|
pub fn applicable(&self, target: Target) -> bool {
|
||||||
match (&self.pattern, target) {
|
match (&self.pattern, target) {
|
||||||
(Pattern::Node(id), Target::Node(node)) => *id == node.id(),
|
(Some(Pattern::Node(id)), Target::Node(node)) => *id == node.id(),
|
||||||
(Pattern::Regex(_), Target::Text(_)) => true,
|
(Some(Pattern::Regex(_)), Target::Text(_)) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -998,12 +1000,13 @@ impl Recipe {
|
|||||||
target: Target,
|
target: Target,
|
||||||
) -> SourceResult<Option<Content>> {
|
) -> SourceResult<Option<Content>> {
|
||||||
let content = match (target, &self.pattern) {
|
let content = match (target, &self.pattern) {
|
||||||
(Target::Node(node), &Pattern::Node(id)) if node.id() == id => {
|
(Target::Node(node), Some(Pattern::Node(id))) if node.id() == *id => {
|
||||||
let node = node.to::<dyn Show>().unwrap().unguard_parts(sel);
|
self.transform.apply(world, self.span, || {
|
||||||
self.call(world, || Value::Content(node))?
|
Value::Content(node.to::<dyn Show>().unwrap().unguard_parts(sel))
|
||||||
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
(Target::Text(text), Pattern::Regex(regex)) => {
|
(Target::Text(text), Some(Pattern::Regex(regex))) => {
|
||||||
let make = world.config().items.text;
|
let make = world.config().items.text;
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
@ -1014,7 +1017,10 @@ impl Recipe {
|
|||||||
result.push(make(text[cursor..start].into()));
|
result.push(make(text[cursor..start].into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(self.call(world, || Value::Str(mat.as_str().into()))?);
|
let transformed = self
|
||||||
|
.transform
|
||||||
|
.apply(world, self.span, || Value::Str(mat.as_str().into()))?;
|
||||||
|
result.push(transformed);
|
||||||
cursor = mat.end();
|
cursor = mat.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1035,24 +1041,10 @@ impl Recipe {
|
|||||||
Ok(Some(content.styled_with_entry(StyleEntry::Guard(sel))))
|
Ok(Some(content.styled_with_entry(StyleEntry::Guard(sel))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call the recipe function, with the argument if desired.
|
|
||||||
fn call<F>(&self, world: Tracked<dyn World>, arg: F) -> SourceResult<Content>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Value,
|
|
||||||
{
|
|
||||||
let args = if self.func.v.argc() == Some(0) {
|
|
||||||
Args::new(self.func.span, [])
|
|
||||||
} else {
|
|
||||||
Args::new(self.func.span, [arg()])
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(self.func.v.call_detached(world, args)?.display(world))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this recipe is for the given node.
|
/// Whether this recipe is for the given node.
|
||||||
pub fn is_of(&self, node: NodeId) -> bool {
|
pub fn is_of(&self, node: NodeId) -> bool {
|
||||||
match self.pattern {
|
match self.pattern {
|
||||||
Pattern::Node(id) => id == node,
|
Some(Pattern::Node(id)) => id == node,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1060,7 +1052,7 @@ impl Recipe {
|
|||||||
|
|
||||||
impl Debug for Recipe {
|
impl Debug for Recipe {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Recipe matching {:?} from {:?}", self.pattern, self.func.span)
|
write!(f, "Recipe matching {:?}", self.pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1080,6 +1072,36 @@ impl Pattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A show rule transformation that can be applied to a match.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
|
pub enum Transform {
|
||||||
|
/// Replacement content.
|
||||||
|
Content(Content),
|
||||||
|
/// A function to apply to the match.
|
||||||
|
Func(Func),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform {
|
||||||
|
/// Apply the transform.
|
||||||
|
pub fn apply<F>(
|
||||||
|
&self,
|
||||||
|
world: Tracked<dyn World>,
|
||||||
|
span: Span,
|
||||||
|
arg: F,
|
||||||
|
) -> SourceResult<Content>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Value,
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A target for a show rule recipe.
|
/// A target for a show rule recipe.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum Target<'a> {
|
pub enum Target<'a> {
|
||||||
@ -1104,30 +1126,25 @@ pub trait Show: 'static + Sync + Send {
|
|||||||
/// Unguard nested content against recursive show rules.
|
/// Unguard nested content against recursive show rules.
|
||||||
fn unguard_parts(&self, sel: Selector) -> Content;
|
fn unguard_parts(&self, sel: Selector) -> Content;
|
||||||
|
|
||||||
/// Access a field on this node.
|
|
||||||
fn field(&self, name: &str) -> Option<Value>;
|
|
||||||
|
|
||||||
/// The base recipe for this node that is executed if there is no
|
/// The base recipe for this node that is executed if there is no
|
||||||
/// user-defined show rule.
|
/// user-defined show rule.
|
||||||
fn realize(
|
fn show(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Content>;
|
) -> SourceResult<Content>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post-process a node after it was realized.
|
||||||
|
#[capability]
|
||||||
|
pub trait Finalize: 'static + Sync + Send {
|
||||||
/// Finalize this node given the realization of a base or user recipe. Use
|
/// Finalize this node given the realization of a base or user recipe. Use
|
||||||
/// this for effects that should work even in the face of a user-defined
|
/// this for effects that should work even in the face of a user-defined
|
||||||
/// show rule, for example:
|
/// show rule, for example the linking behaviour of a link node.
|
||||||
/// - Application of general settable properties
|
|
||||||
///
|
|
||||||
/// Defaults to just the realized content.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn finalize(
|
fn finalize(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
realized: Content,
|
realized: Content,
|
||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content>;
|
||||||
Ok(realized)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,7 @@ primitive! { Str: "string", Str }
|
|||||||
primitive! { Content: "content",
|
primitive! { Content: "content",
|
||||||
Content,
|
Content,
|
||||||
None => Content::empty(),
|
None => Content::empty(),
|
||||||
Str(text) => Content::text(text)
|
Str(text) => item!(text)(text.into())
|
||||||
}
|
}
|
||||||
primitive! { Array: "array", Array }
|
primitive! { Array: "array", Array }
|
||||||
primitive! { Dict: "dictionary", Dict }
|
primitive! { Dict: "dictionary", Dict }
|
||||||
|
@ -63,11 +63,11 @@ impl Markup {
|
|||||||
/// A single piece of markup.
|
/// A single piece of markup.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum MarkupNode {
|
pub enum MarkupNode {
|
||||||
/// Whitespace containing less than two newlines.
|
/// Whitespace.
|
||||||
Space(Space),
|
Space(Space),
|
||||||
/// A forced line break.
|
/// A forced line break: `\`.
|
||||||
Linebreak(Linebreak),
|
Linebreak(Linebreak),
|
||||||
/// Plain text.
|
/// Plain text without markup.
|
||||||
Text(Text),
|
Text(Text),
|
||||||
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
||||||
Escape(Escape),
|
Escape(Escape),
|
||||||
@ -76,9 +76,9 @@ pub enum MarkupNode {
|
|||||||
Shorthand(Shorthand),
|
Shorthand(Shorthand),
|
||||||
/// A smart quote: `'` or `"`.
|
/// A smart quote: `'` or `"`.
|
||||||
SmartQuote(SmartQuote),
|
SmartQuote(SmartQuote),
|
||||||
/// Strong markup: `*Strong*`.
|
/// Strong content: `*Strong*`.
|
||||||
Strong(Strong),
|
Strong(Strong),
|
||||||
/// Emphasized markup: `_Emphasized_`.
|
/// Emphasized content: `_Emphasized_`.
|
||||||
Emph(Emph),
|
Emph(Emph),
|
||||||
/// A raw block with optional syntax highlighting: `` `...` ``.
|
/// A raw block with optional syntax highlighting: `` `...` ``.
|
||||||
Raw(Raw),
|
Raw(Raw),
|
||||||
@ -171,7 +171,7 @@ node! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// Plain text.
|
/// Plain text without markup.
|
||||||
Text
|
Text
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,12 +367,12 @@ impl ListItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// An item in an enumeration (ordered list): `1. ...`.
|
/// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
|
||||||
EnumItem
|
EnumItem
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumItem {
|
impl EnumItem {
|
||||||
/// The number, if any.
|
/// The explicit numbering, if any: `23.`.
|
||||||
pub fn number(&self) -> Option<usize> {
|
pub fn number(&self) -> Option<usize> {
|
||||||
self.0.children().find_map(|node| match node.kind() {
|
self.0.children().find_map(|node| match node.kind() {
|
||||||
NodeKind::EnumNumbering(num) => Some(*num),
|
NodeKind::EnumNumbering(num) => Some(*num),
|
||||||
@ -434,9 +434,9 @@ pub enum MathNode {
|
|||||||
Linebreak(Linebreak),
|
Linebreak(Linebreak),
|
||||||
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
||||||
Escape(Escape),
|
Escape(Escape),
|
||||||
/// A atom: `x`, `+`, `12`.
|
/// An atom: `x`, `+`, `12`.
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
/// A base with an optional sub- and superscript: `a_1^2`.
|
/// A base with optional sub- and superscripts: `a_1^2`.
|
||||||
Script(Script),
|
Script(Script),
|
||||||
/// A fraction: `x/2`.
|
/// A fraction: `x/2`.
|
||||||
Frac(Frac),
|
Frac(Frac),
|
||||||
@ -565,9 +565,9 @@ pub enum Expr {
|
|||||||
Content(ContentBlock),
|
Content(ContentBlock),
|
||||||
/// A grouped expression: `(1 + 2)`.
|
/// A grouped expression: `(1 + 2)`.
|
||||||
Parenthesized(Parenthesized),
|
Parenthesized(Parenthesized),
|
||||||
/// An array expression: `(1, "hi", 12cm)`.
|
/// An array: `(1, "hi", 12cm)`.
|
||||||
Array(Array),
|
Array(Array),
|
||||||
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
||||||
Dict(Dict),
|
Dict(Dict),
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
Unary(Unary),
|
Unary(Unary),
|
||||||
@ -579,16 +579,14 @@ pub enum Expr {
|
|||||||
FuncCall(FuncCall),
|
FuncCall(FuncCall),
|
||||||
/// An invocation of a method: `array.push(v)`.
|
/// An invocation of a method: `array.push(v)`.
|
||||||
MethodCall(MethodCall),
|
MethodCall(MethodCall),
|
||||||
/// A closure expression: `(x, y) => z`.
|
/// A closure: `(x, y) => z`.
|
||||||
Closure(Closure),
|
Closure(Closure),
|
||||||
/// A let binding: `let x = 1`.
|
/// A let binding: `let x = 1`.
|
||||||
Let(LetBinding),
|
Let(LetBinding),
|
||||||
/// A set rule: `set text(...)`.
|
/// A set rule: `set text(...)`.
|
||||||
Set(SetRule),
|
Set(SetRule),
|
||||||
/// A show rule: `show node: heading as [*{nody.body}*]`.
|
/// A show rule: `show heading: it => [*{it.body}*]`.
|
||||||
Show(ShowRule),
|
Show(ShowRule),
|
||||||
/// A wrap rule: `wrap body in columns(2, body)`.
|
|
||||||
Wrap(WrapRule),
|
|
||||||
/// An if-else conditional: `if x { y } else { z }`.
|
/// An if-else conditional: `if x { y } else { z }`.
|
||||||
Conditional(Conditional),
|
Conditional(Conditional),
|
||||||
/// A while loop: `while x { y }`.
|
/// A while loop: `while x { y }`.
|
||||||
@ -599,12 +597,12 @@ pub enum Expr {
|
|||||||
Import(ModuleImport),
|
Import(ModuleImport),
|
||||||
/// A module include: `include "chapter1.typ"`.
|
/// A module include: `include "chapter1.typ"`.
|
||||||
Include(ModuleInclude),
|
Include(ModuleInclude),
|
||||||
/// A break statement: `break`.
|
/// A break from a loop: `break`.
|
||||||
Break(BreakStmt),
|
Break(LoopBreak),
|
||||||
/// A continue statement: `continue`.
|
/// A continue in a loop: `continue`.
|
||||||
Continue(ContinueStmt),
|
Continue(LoopContinue),
|
||||||
/// A return statement: `return`, `return x + 1`.
|
/// A return from a function: `return`, `return x + 1`.
|
||||||
Return(ReturnStmt),
|
Return(FuncReturn),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedNode for Expr {
|
impl TypedNode for Expr {
|
||||||
@ -625,15 +623,14 @@ impl TypedNode for Expr {
|
|||||||
NodeKind::LetBinding => node.cast().map(Self::Let),
|
NodeKind::LetBinding => node.cast().map(Self::Let),
|
||||||
NodeKind::SetRule => node.cast().map(Self::Set),
|
NodeKind::SetRule => node.cast().map(Self::Set),
|
||||||
NodeKind::ShowRule => node.cast().map(Self::Show),
|
NodeKind::ShowRule => node.cast().map(Self::Show),
|
||||||
NodeKind::WrapRule => node.cast().map(Self::Wrap),
|
|
||||||
NodeKind::Conditional => node.cast().map(Self::Conditional),
|
NodeKind::Conditional => node.cast().map(Self::Conditional),
|
||||||
NodeKind::WhileLoop => node.cast().map(Self::While),
|
NodeKind::WhileLoop => node.cast().map(Self::While),
|
||||||
NodeKind::ForLoop => node.cast().map(Self::For),
|
NodeKind::ForLoop => node.cast().map(Self::For),
|
||||||
NodeKind::ModuleImport => node.cast().map(Self::Import),
|
NodeKind::ModuleImport => node.cast().map(Self::Import),
|
||||||
NodeKind::ModuleInclude => node.cast().map(Self::Include),
|
NodeKind::ModuleInclude => node.cast().map(Self::Include),
|
||||||
NodeKind::BreakStmt => node.cast().map(Self::Break),
|
NodeKind::LoopBreak => node.cast().map(Self::Break),
|
||||||
NodeKind::ContinueStmt => node.cast().map(Self::Continue),
|
NodeKind::LoopContinue => node.cast().map(Self::Continue),
|
||||||
NodeKind::ReturnStmt => node.cast().map(Self::Return),
|
NodeKind::FuncReturn => node.cast().map(Self::Return),
|
||||||
_ => node.cast().map(Self::Lit),
|
_ => node.cast().map(Self::Lit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,7 +653,6 @@ impl TypedNode for Expr {
|
|||||||
Self::Let(v) => v.as_untyped(),
|
Self::Let(v) => v.as_untyped(),
|
||||||
Self::Set(v) => v.as_untyped(),
|
Self::Set(v) => v.as_untyped(),
|
||||||
Self::Show(v) => v.as_untyped(),
|
Self::Show(v) => v.as_untyped(),
|
||||||
Self::Wrap(v) => v.as_untyped(),
|
|
||||||
Self::Conditional(v) => v.as_untyped(),
|
Self::Conditional(v) => v.as_untyped(),
|
||||||
Self::While(v) => v.as_untyped(),
|
Self::While(v) => v.as_untyped(),
|
||||||
Self::For(v) => v.as_untyped(),
|
Self::For(v) => v.as_untyped(),
|
||||||
@ -679,7 +675,6 @@ impl Expr {
|
|||||||
| Self::Let(_)
|
| Self::Let(_)
|
||||||
| Self::Set(_)
|
| Self::Set(_)
|
||||||
| Self::Show(_)
|
| Self::Show(_)
|
||||||
| Self::Wrap(_)
|
|
||||||
| Self::Conditional(_)
|
| Self::Conditional(_)
|
||||||
| Self::While(_)
|
| Self::While(_)
|
||||||
| Self::For(_)
|
| Self::For(_)
|
||||||
@ -723,15 +718,15 @@ pub enum LitKind {
|
|||||||
None,
|
None,
|
||||||
/// The auto literal: `auto`.
|
/// The auto literal: `auto`.
|
||||||
Auto,
|
Auto,
|
||||||
/// A boolean literal: `true`, `false`.
|
/// A boolean: `true`, `false`.
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
/// An integer literal: `120`.
|
/// An integer: `120`.
|
||||||
Int(i64),
|
Int(i64),
|
||||||
/// A floating-point literal: `1.2`, `10e-4`.
|
/// A floating-point number: `1.2`, `10e-4`.
|
||||||
Float(f64),
|
Float(f64),
|
||||||
/// A numeric literal with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
|
/// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
|
||||||
Numeric(f64, Unit),
|
Numeric(f64, Unit),
|
||||||
/// A string literal: `"hello!"`.
|
/// A quoted string: `"..."`.
|
||||||
Str(EcoString),
|
Str(EcoString),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,7 +755,7 @@ impl ContentBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A parenthesized expression: `(1 + 2)`.
|
/// A grouped expression: `(1 + 2)`.
|
||||||
Parenthesized
|
Parenthesized
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,7 +848,7 @@ impl TypedNode for DictItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A pair of a name and an expression: `thickness: 3pt`.
|
/// A named pair: `thickness: 3pt`.
|
||||||
Named
|
Named
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,7 +865,7 @@ impl Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A pair of a string key and an expression: `"spacy key": true`.
|
/// A keyed pair: `"spacy key": true`.
|
||||||
Keyed
|
Keyed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1204,7 +1199,7 @@ impl MethodCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// The arguments to a function: `12, draw: false`.
|
/// A function call's argument list: `(12pt, y)`.
|
||||||
Args
|
Args
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1245,7 +1240,7 @@ impl TypedNode for Arg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A closure expression: `(x, y) => z`.
|
/// A closure: `(x, y) => z`.
|
||||||
Closure
|
Closure
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,52 +1342,34 @@ impl SetRule {
|
|||||||
pub fn args(&self) -> Args {
|
pub fn args(&self) -> Args {
|
||||||
self.0.cast_last_child().expect("set rule is missing argument list")
|
self.0.cast_last_child().expect("set rule is missing argument list")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A condition under which the set rule applies.
|
||||||
|
pub fn condition(&self) -> Option<Expr> {
|
||||||
|
self.0
|
||||||
|
.children()
|
||||||
|
.skip_while(|child| child.kind() != &NodeKind::If)
|
||||||
|
.find_map(SyntaxNode::cast)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A show rule: `show node: heading as [*{nody.body}*]`.
|
/// A show rule: `show heading: it => [*{it.body}*]`.
|
||||||
ShowRule
|
ShowRule
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShowRule {
|
impl ShowRule {
|
||||||
/// The binding to assign to.
|
|
||||||
pub fn binding(&self) -> Option<Ident> {
|
|
||||||
let mut children = self.0.children();
|
|
||||||
children
|
|
||||||
.find_map(SyntaxNode::cast)
|
|
||||||
.filter(|_| children.any(|child| child.kind() == &NodeKind::Colon))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The pattern that this rule matches.
|
/// The pattern that this rule matches.
|
||||||
pub fn pattern(&self) -> Expr {
|
pub fn pattern(&self) -> Option<Expr> {
|
||||||
self.0
|
self.0
|
||||||
.children()
|
.children()
|
||||||
.rev()
|
.rev()
|
||||||
.skip_while(|child| child.kind() != &NodeKind::As)
|
.skip_while(|child| child.kind() != &NodeKind::Colon)
|
||||||
.find_map(SyntaxNode::cast)
|
.find_map(SyntaxNode::cast)
|
||||||
.expect("show rule is missing pattern")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The expression that realizes the node.
|
/// The transformation recipe.
|
||||||
pub fn body(&self) -> Expr {
|
pub fn transform(&self) -> Expr {
|
||||||
self.0.cast_last_child().expect("show rule is missing body")
|
self.0.cast_last_child().expect("show rule is missing transform")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node! {
|
|
||||||
/// A wrap rule: `wrap body in columns(2, body)`.
|
|
||||||
WrapRule
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapRule {
|
|
||||||
/// The binding to assign the remaining markup to.
|
|
||||||
pub fn binding(&self) -> Ident {
|
|
||||||
self.0.cast_first_child().expect("wrap rule is missing binding")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The expression to evaluate.
|
|
||||||
pub fn body(&self) -> Expr {
|
|
||||||
self.0.cast_last_child().expect("wrap rule is missing body")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1462,7 +1439,7 @@ impl ForLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A for-in loop: `for x in y { z }`.
|
/// A for loop's destructuring pattern: `x` or `x, y`.
|
||||||
ForPattern
|
ForPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1533,21 +1510,21 @@ impl ModuleInclude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A break expression: `break`.
|
/// A break from a loop: `break`.
|
||||||
BreakStmt
|
LoopBreak
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A continue expression: `continue`.
|
/// A continue in a loop: `continue`.
|
||||||
ContinueStmt
|
LoopContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A return expression: `return`, `return x + 1`.
|
/// A return from a function: `return`, `return x + 1`.
|
||||||
ReturnStmt
|
FuncReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnStmt {
|
impl FuncReturn {
|
||||||
/// The expression to return.
|
/// The expression to return.
|
||||||
pub fn body(&self) -> Option<Expr> {
|
pub fn body(&self) -> Option<Expr> {
|
||||||
self.0.cast_last_child()
|
self.0.cast_last_child()
|
||||||
@ -1555,7 +1532,7 @@ impl ReturnStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// An identifier.
|
/// An identifier: `it`.
|
||||||
Ident
|
Ident
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,6 @@ impl Category {
|
|||||||
NodeKind::Let => Some(Category::Keyword),
|
NodeKind::Let => Some(Category::Keyword),
|
||||||
NodeKind::Set => Some(Category::Keyword),
|
NodeKind::Set => Some(Category::Keyword),
|
||||||
NodeKind::Show => Some(Category::Keyword),
|
NodeKind::Show => Some(Category::Keyword),
|
||||||
NodeKind::Wrap => Some(Category::Keyword),
|
|
||||||
NodeKind::If => Some(Category::Keyword),
|
NodeKind::If => Some(Category::Keyword),
|
||||||
NodeKind::Else => Some(Category::Keyword),
|
NodeKind::Else => Some(Category::Keyword),
|
||||||
NodeKind::For => Some(Category::Keyword),
|
NodeKind::For => Some(Category::Keyword),
|
||||||
@ -269,7 +268,6 @@ impl Category {
|
|||||||
NodeKind::Import => Some(Category::Keyword),
|
NodeKind::Import => Some(Category::Keyword),
|
||||||
NodeKind::Include => Some(Category::Keyword),
|
NodeKind::Include => Some(Category::Keyword),
|
||||||
NodeKind::From => Some(Category::Keyword),
|
NodeKind::From => Some(Category::Keyword),
|
||||||
NodeKind::As => Some(Category::Keyword),
|
|
||||||
|
|
||||||
NodeKind::Markup { .. } => match parent.kind() {
|
NodeKind::Markup { .. } => match parent.kind() {
|
||||||
NodeKind::DescItem
|
NodeKind::DescItem
|
||||||
@ -316,8 +314,7 @@ impl Category {
|
|||||||
if parent
|
if parent
|
||||||
.children()
|
.children()
|
||||||
.rev()
|
.rev()
|
||||||
.skip_while(|child| child.kind() != &NodeKind::As)
|
.skip_while(|child| child.kind() != &NodeKind::Colon)
|
||||||
.take_while(|child| child.kind() != &NodeKind::Colon)
|
|
||||||
.find(|c| matches!(c.kind(), NodeKind::Ident(_)))
|
.find(|c| matches!(c.kind(), NodeKind::Ident(_)))
|
||||||
.map_or(false, |ident| std::ptr::eq(ident, child)) =>
|
.map_or(false, |ident| std::ptr::eq(ident, child)) =>
|
||||||
{
|
{
|
||||||
@ -349,7 +346,6 @@ impl Category {
|
|||||||
NodeKind::LetBinding => None,
|
NodeKind::LetBinding => None,
|
||||||
NodeKind::SetRule => None,
|
NodeKind::SetRule => None,
|
||||||
NodeKind::ShowRule => None,
|
NodeKind::ShowRule => None,
|
||||||
NodeKind::WrapRule => None,
|
|
||||||
NodeKind::Conditional => None,
|
NodeKind::Conditional => None,
|
||||||
NodeKind::WhileLoop => None,
|
NodeKind::WhileLoop => None,
|
||||||
NodeKind::ForLoop => None,
|
NodeKind::ForLoop => None,
|
||||||
@ -357,9 +353,9 @@ impl Category {
|
|||||||
NodeKind::ModuleImport => None,
|
NodeKind::ModuleImport => None,
|
||||||
NodeKind::ImportItems => None,
|
NodeKind::ImportItems => None,
|
||||||
NodeKind::ModuleInclude => None,
|
NodeKind::ModuleInclude => None,
|
||||||
NodeKind::BreakStmt => None,
|
NodeKind::LoopBreak => None,
|
||||||
NodeKind::ContinueStmt => None,
|
NodeKind::LoopContinue => None,
|
||||||
NodeKind::ReturnStmt => None,
|
NodeKind::FuncReturn => None,
|
||||||
|
|
||||||
NodeKind::Error(_, _) => Some(Category::Error),
|
NodeKind::Error(_, _) => Some(Category::Error),
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,6 @@ pub enum NodeKind {
|
|||||||
Set,
|
Set,
|
||||||
/// The `show` keyword.
|
/// The `show` keyword.
|
||||||
Show,
|
Show,
|
||||||
/// The `wrap` keyword.
|
|
||||||
Wrap,
|
|
||||||
/// The `if` keyword.
|
/// The `if` keyword.
|
||||||
If,
|
If,
|
||||||
/// The `else` keyword.
|
/// The `else` keyword.
|
||||||
@ -130,8 +128,6 @@ pub enum NodeKind {
|
|||||||
Include,
|
Include,
|
||||||
/// The `from` keyword.
|
/// The `from` keyword.
|
||||||
From,
|
From,
|
||||||
/// The `as` keyword.
|
|
||||||
As,
|
|
||||||
|
|
||||||
/// Markup of which all lines must have a minimal indentation.
|
/// Markup of which all lines must have a minimal indentation.
|
||||||
///
|
///
|
||||||
@ -139,7 +135,7 @@ pub enum NodeKind {
|
|||||||
/// started, but to the right of which column all markup elements must be,
|
/// started, but to the right of which column all markup elements must be,
|
||||||
/// so it is zero except inside indent-aware constructs like lists.
|
/// so it is zero except inside indent-aware constructs like lists.
|
||||||
Markup { min_indent: usize },
|
Markup { min_indent: usize },
|
||||||
/// Consecutive text without markup.
|
/// Plain text without markup.
|
||||||
Text(EcoString),
|
Text(EcoString),
|
||||||
/// A forced line break: `\`.
|
/// A forced line break: `\`.
|
||||||
Linebreak,
|
Linebreak,
|
||||||
@ -150,9 +146,9 @@ pub enum NodeKind {
|
|||||||
Shorthand(char),
|
Shorthand(char),
|
||||||
/// A smart quote: `'` or `"`.
|
/// A smart quote: `'` or `"`.
|
||||||
SmartQuote { double: bool },
|
SmartQuote { double: bool },
|
||||||
/// Strong markup: `*Strong*`.
|
/// Strong content: `*Strong*`.
|
||||||
Strong,
|
Strong,
|
||||||
/// Emphasized markup: `_Emphasized_`.
|
/// Emphasized content: `_Emphasized_`.
|
||||||
Emph,
|
Emph,
|
||||||
/// A raw block with optional syntax highlighting: `` `...` ``.
|
/// A raw block with optional syntax highlighting: `` `...` ``.
|
||||||
Raw(Arc<RawKind>),
|
Raw(Arc<RawKind>),
|
||||||
@ -164,26 +160,26 @@ pub enum NodeKind {
|
|||||||
Ref(EcoString),
|
Ref(EcoString),
|
||||||
/// A section heading: `= Introduction`.
|
/// A section heading: `= Introduction`.
|
||||||
Heading,
|
Heading,
|
||||||
/// An item of an unordered list: `- ...`.
|
/// An item in an unordered list: `- ...`.
|
||||||
ListItem,
|
ListItem,
|
||||||
/// An item of an enumeration (ordered list): `+ ...` or `1. ...`.
|
/// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
|
||||||
EnumItem,
|
EnumItem,
|
||||||
/// An explicit enumeration numbering: `23.`.
|
/// An explicit enumeration numbering: `23.`.
|
||||||
EnumNumbering(usize),
|
EnumNumbering(usize),
|
||||||
/// An item of a description list: `/ Term: Details.
|
/// An item in a description list: `/ Term: Details`.
|
||||||
DescItem,
|
DescItem,
|
||||||
/// A math formula: `$x$`, `$ x^2 $`.
|
/// A mathematical formula: `$x$`, `$ x^2 $`.
|
||||||
Math,
|
Math,
|
||||||
/// An atom in a math formula: `x`, `+`, `12`.
|
/// An atom in a formula: `x`, `+`, `12`.
|
||||||
Atom(EcoString),
|
Atom(EcoString),
|
||||||
/// A base with optional sub- and superscript in a math formula: `a_1^2`.
|
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
|
||||||
Script,
|
Script,
|
||||||
/// A fraction in a math formula: `x/2`.
|
/// A fraction in a formula: `x/2`.
|
||||||
Frac,
|
Frac,
|
||||||
/// An alignment indicator in a math formula: `&`, `&&`.
|
/// An alignment indicator in a formula: `&`, `&&`.
|
||||||
Align,
|
Align,
|
||||||
|
|
||||||
/// An identifier: `center`.
|
/// An identifier: `it`.
|
||||||
Ident(EcoString),
|
Ident(EcoString),
|
||||||
/// A boolean: `true`, `false`.
|
/// A boolean: `true`, `false`.
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
@ -219,9 +215,9 @@ pub enum NodeKind {
|
|||||||
FuncCall,
|
FuncCall,
|
||||||
/// An invocation of a method: `array.push(v)`.
|
/// An invocation of a method: `array.push(v)`.
|
||||||
MethodCall,
|
MethodCall,
|
||||||
/// A function call's argument list: `(x, y)`.
|
/// A function call's argument list: `(12pt, y)`.
|
||||||
Args,
|
Args,
|
||||||
/// Spreaded arguments or a argument sink: `..x`.
|
/// Spreaded arguments or an argument sink: `..x`.
|
||||||
Spread,
|
Spread,
|
||||||
/// A closure: `(x, y) => z`.
|
/// A closure: `(x, y) => z`.
|
||||||
Closure,
|
Closure,
|
||||||
@ -231,15 +227,13 @@ pub enum NodeKind {
|
|||||||
LetBinding,
|
LetBinding,
|
||||||
/// A set rule: `set text(...)`.
|
/// A set rule: `set text(...)`.
|
||||||
SetRule,
|
SetRule,
|
||||||
/// A show rule: `show node: heading as [*{nody.body}*]`.
|
/// A show rule: `show heading: it => [*{it.body}*]`.
|
||||||
ShowRule,
|
ShowRule,
|
||||||
/// A wrap rule: `wrap body in columns(2, body)`.
|
|
||||||
WrapRule,
|
|
||||||
/// An if-else conditional: `if x { y } else { z }`.
|
/// An if-else conditional: `if x { y } else { z }`.
|
||||||
Conditional,
|
Conditional,
|
||||||
/// A while loop: `while x { ... }`.
|
/// A while loop: `while x { y }`.
|
||||||
WhileLoop,
|
WhileLoop,
|
||||||
/// A for loop: `for x in y { ... }`.
|
/// A for loop: `for x in y { z }`.
|
||||||
ForLoop,
|
ForLoop,
|
||||||
/// A for loop's destructuring pattern: `x` or `x, y`.
|
/// A for loop's destructuring pattern: `x` or `x, y`.
|
||||||
ForPattern,
|
ForPattern,
|
||||||
@ -249,12 +243,12 @@ pub enum NodeKind {
|
|||||||
ImportItems,
|
ImportItems,
|
||||||
/// A module include: `include "chapter1.typ"`.
|
/// A module include: `include "chapter1.typ"`.
|
||||||
ModuleInclude,
|
ModuleInclude,
|
||||||
/// A break statement: `break`.
|
/// A break from a loop: `break`.
|
||||||
BreakStmt,
|
LoopBreak,
|
||||||
/// A continue statement: `continue`.
|
/// A continue in a loop: `continue`.
|
||||||
ContinueStmt,
|
LoopContinue,
|
||||||
/// A return statement: `return x + 1`.
|
/// A return from a function: `return`, `return x + 1`.
|
||||||
ReturnStmt,
|
FuncReturn,
|
||||||
|
|
||||||
/// An invalid sequence of characters.
|
/// An invalid sequence of characters.
|
||||||
Error(ErrorPos, EcoString),
|
Error(ErrorPos, EcoString),
|
||||||
@ -367,7 +361,6 @@ impl NodeKind {
|
|||||||
Self::Let => "keyword `let`",
|
Self::Let => "keyword `let`",
|
||||||
Self::Set => "keyword `set`",
|
Self::Set => "keyword `set`",
|
||||||
Self::Show => "keyword `show`",
|
Self::Show => "keyword `show`",
|
||||||
Self::Wrap => "keyword `wrap`",
|
|
||||||
Self::If => "keyword `if`",
|
Self::If => "keyword `if`",
|
||||||
Self::Else => "keyword `else`",
|
Self::Else => "keyword `else`",
|
||||||
Self::For => "keyword `for`",
|
Self::For => "keyword `for`",
|
||||||
@ -379,7 +372,6 @@ impl NodeKind {
|
|||||||
Self::Import => "keyword `import`",
|
Self::Import => "keyword `import`",
|
||||||
Self::Include => "keyword `include`",
|
Self::Include => "keyword `include`",
|
||||||
Self::From => "keyword `from`",
|
Self::From => "keyword `from`",
|
||||||
Self::As => "keyword `as`",
|
|
||||||
Self::Markup { .. } => "markup",
|
Self::Markup { .. } => "markup",
|
||||||
Self::Text(_) => "text",
|
Self::Text(_) => "text",
|
||||||
Self::Linebreak => "linebreak",
|
Self::Linebreak => "linebreak",
|
||||||
@ -426,7 +418,6 @@ impl NodeKind {
|
|||||||
Self::LetBinding => "`let` expression",
|
Self::LetBinding => "`let` expression",
|
||||||
Self::SetRule => "`set` expression",
|
Self::SetRule => "`set` expression",
|
||||||
Self::ShowRule => "`show` expression",
|
Self::ShowRule => "`show` expression",
|
||||||
Self::WrapRule => "`wrap` expression",
|
|
||||||
Self::Conditional => "`if` expression",
|
Self::Conditional => "`if` expression",
|
||||||
Self::WhileLoop => "while-loop expression",
|
Self::WhileLoop => "while-loop expression",
|
||||||
Self::ForLoop => "for-loop expression",
|
Self::ForLoop => "for-loop expression",
|
||||||
@ -434,9 +425,9 @@ impl NodeKind {
|
|||||||
Self::ModuleImport => "`import` expression",
|
Self::ModuleImport => "`import` expression",
|
||||||
Self::ImportItems => "import items",
|
Self::ImportItems => "import items",
|
||||||
Self::ModuleInclude => "`include` expression",
|
Self::ModuleInclude => "`include` expression",
|
||||||
Self::BreakStmt => "`break` expression",
|
Self::LoopBreak => "`break` expression",
|
||||||
Self::ContinueStmt => "`continue` expression",
|
Self::LoopContinue => "`continue` expression",
|
||||||
Self::ReturnStmt => "`return` expression",
|
Self::FuncReturn => "`return` expression",
|
||||||
Self::Error(_, _) => "syntax error",
|
Self::Error(_, _) => "syntax error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,7 +479,6 @@ impl Hash for NodeKind {
|
|||||||
Self::Let => {}
|
Self::Let => {}
|
||||||
Self::Set => {}
|
Self::Set => {}
|
||||||
Self::Show => {}
|
Self::Show => {}
|
||||||
Self::Wrap => {}
|
|
||||||
Self::If => {}
|
Self::If => {}
|
||||||
Self::Else => {}
|
Self::Else => {}
|
||||||
Self::For => {}
|
Self::For => {}
|
||||||
@ -500,7 +490,6 @@ impl Hash for NodeKind {
|
|||||||
Self::Import => {}
|
Self::Import => {}
|
||||||
Self::Include => {}
|
Self::Include => {}
|
||||||
Self::From => {}
|
Self::From => {}
|
||||||
Self::As => {}
|
|
||||||
Self::Markup { min_indent } => min_indent.hash(state),
|
Self::Markup { min_indent } => min_indent.hash(state),
|
||||||
Self::Text(s) => s.hash(state),
|
Self::Text(s) => s.hash(state),
|
||||||
Self::Linebreak => {}
|
Self::Linebreak => {}
|
||||||
@ -548,7 +537,6 @@ impl Hash for NodeKind {
|
|||||||
Self::LetBinding => {}
|
Self::LetBinding => {}
|
||||||
Self::SetRule => {}
|
Self::SetRule => {}
|
||||||
Self::ShowRule => {}
|
Self::ShowRule => {}
|
||||||
Self::WrapRule => {}
|
|
||||||
Self::Conditional => {}
|
Self::Conditional => {}
|
||||||
Self::WhileLoop => {}
|
Self::WhileLoop => {}
|
||||||
Self::ForLoop => {}
|
Self::ForLoop => {}
|
||||||
@ -556,9 +544,9 @@ impl Hash for NodeKind {
|
|||||||
Self::ModuleImport => {}
|
Self::ModuleImport => {}
|
||||||
Self::ImportItems => {}
|
Self::ImportItems => {}
|
||||||
Self::ModuleInclude => {}
|
Self::ModuleInclude => {}
|
||||||
Self::BreakStmt => {}
|
Self::LoopBreak => {}
|
||||||
Self::ContinueStmt => {}
|
Self::LoopContinue => {}
|
||||||
Self::ReturnStmt => {}
|
Self::FuncReturn => {}
|
||||||
Self::Error(pos, msg) => (pos, msg).hash(state),
|
Self::Error(pos, msg) => (pos, msg).hash(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,6 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a markup node.
|
|
||||||
fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
||||||
let Some(token) = p.peek() else { return };
|
let Some(token) = p.peek() else { return };
|
||||||
match token {
|
match token {
|
||||||
@ -245,10 +244,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
|||||||
NodeKind::Eq => heading(p, *at_start),
|
NodeKind::Eq => heading(p, *at_start),
|
||||||
|
|
||||||
// Lists.
|
// Lists.
|
||||||
NodeKind::Minus => list_node(p, *at_start),
|
NodeKind::Minus => list_item(p, *at_start),
|
||||||
NodeKind::Plus | NodeKind::EnumNumbering(_) => enum_node(p, *at_start),
|
NodeKind::Plus | NodeKind::EnumNumbering(_) => enum_item(p, *at_start),
|
||||||
NodeKind::Slash => {
|
NodeKind::Slash => {
|
||||||
desc_node(p, *at_start).ok();
|
desc_item(p, *at_start).ok();
|
||||||
}
|
}
|
||||||
NodeKind::Colon => {
|
NodeKind::Colon => {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
@ -261,7 +260,6 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
|||||||
| NodeKind::Let
|
| NodeKind::Let
|
||||||
| NodeKind::Set
|
| NodeKind::Set
|
||||||
| NodeKind::Show
|
| NodeKind::Show
|
||||||
| NodeKind::Wrap
|
|
||||||
| NodeKind::If
|
| NodeKind::If
|
||||||
| NodeKind::While
|
| NodeKind::While
|
||||||
| NodeKind::For
|
| NodeKind::For
|
||||||
@ -282,7 +280,6 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
|||||||
*at_start = false;
|
*at_start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse strong content.
|
|
||||||
fn strong(p: &mut Parser) {
|
fn strong(p: &mut Parser) {
|
||||||
p.perform(NodeKind::Strong, |p| {
|
p.perform(NodeKind::Strong, |p| {
|
||||||
p.start_group(Group::Strong);
|
p.start_group(Group::Strong);
|
||||||
@ -291,7 +288,6 @@ fn strong(p: &mut Parser) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse emphasized content.
|
|
||||||
fn emph(p: &mut Parser) {
|
fn emph(p: &mut Parser) {
|
||||||
p.perform(NodeKind::Emph, |p| {
|
p.perform(NodeKind::Emph, |p| {
|
||||||
p.start_group(Group::Emph);
|
p.start_group(Group::Emph);
|
||||||
@ -300,7 +296,6 @@ fn emph(p: &mut Parser) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a heading.
|
|
||||||
fn heading(p: &mut Parser, at_start: bool) {
|
fn heading(p: &mut Parser, at_start: bool) {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
let current_start = p.current_start();
|
let current_start = p.current_start();
|
||||||
@ -317,8 +312,7 @@ fn heading(p: &mut Parser, at_start: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single list item.
|
fn list_item(p: &mut Parser, at_start: bool) {
|
||||||
fn list_node(p: &mut Parser, at_start: bool) {
|
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
let text: EcoString = p.peek_src().into();
|
let text: EcoString = p.peek_src().into();
|
||||||
p.assert(NodeKind::Minus);
|
p.assert(NodeKind::Minus);
|
||||||
@ -332,8 +326,7 @@ fn list_node(p: &mut Parser, at_start: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single enum item.
|
fn enum_item(p: &mut Parser, at_start: bool) {
|
||||||
fn enum_node(p: &mut Parser, at_start: bool) {
|
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
let text: EcoString = p.peek_src().into();
|
let text: EcoString = p.peek_src().into();
|
||||||
p.eat();
|
p.eat();
|
||||||
@ -347,8 +340,7 @@ fn enum_node(p: &mut Parser, at_start: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single description list item.
|
fn desc_item(p: &mut Parser, at_start: bool) -> ParseResult {
|
||||||
fn desc_node(p: &mut Parser, at_start: bool) -> ParseResult {
|
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
let text: EcoString = p.peek_src().into();
|
let text: EcoString = p.peek_src().into();
|
||||||
p.eat();
|
p.eat();
|
||||||
@ -366,7 +358,6 @@ fn desc_node(p: &mut Parser, at_start: bool) -> ParseResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression within a markup mode.
|
|
||||||
fn markup_expr(p: &mut Parser) {
|
fn markup_expr(p: &mut Parser) {
|
||||||
// Does the expression need termination or can content follow directly?
|
// Does the expression need termination or can content follow directly?
|
||||||
let stmt = matches!(
|
let stmt = matches!(
|
||||||
@ -375,7 +366,6 @@ fn markup_expr(p: &mut Parser) {
|
|||||||
NodeKind::Let
|
NodeKind::Let
|
||||||
| NodeKind::Set
|
| NodeKind::Set
|
||||||
| NodeKind::Show
|
| NodeKind::Show
|
||||||
| NodeKind::Wrap
|
|
||||||
| NodeKind::Import
|
| NodeKind::Import
|
||||||
| NodeKind::Include
|
| NodeKind::Include
|
||||||
)
|
)
|
||||||
@ -389,7 +379,6 @@ fn markup_expr(p: &mut Parser) {
|
|||||||
p.end_group();
|
p.end_group();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse math.
|
|
||||||
fn math(p: &mut Parser) {
|
fn math(p: &mut Parser) {
|
||||||
p.perform(NodeKind::Math, |p| {
|
p.perform(NodeKind::Math, |p| {
|
||||||
p.start_group(Group::Math);
|
p.start_group(Group::Math);
|
||||||
@ -400,12 +389,10 @@ fn math(p: &mut Parser) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a math node.
|
|
||||||
fn math_node(p: &mut Parser) {
|
fn math_node(p: &mut Parser) {
|
||||||
math_node_prec(p, 0, None)
|
math_node_prec(p, 0, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a math node with operators having at least the minimum precedence.
|
|
||||||
fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) {
|
fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
math_primary(p);
|
math_primary(p);
|
||||||
@ -457,19 +444,18 @@ fn math_primary(p: &mut Parser) {
|
|||||||
| NodeKind::Ident(_) => p.eat(),
|
| NodeKind::Ident(_) => p.eat(),
|
||||||
|
|
||||||
// Groups.
|
// Groups.
|
||||||
NodeKind::LeftParen => group(p, Group::Paren, '(', ')'),
|
NodeKind::LeftParen => math_group(p, Group::Paren, '(', ')'),
|
||||||
NodeKind::LeftBracket => group(p, Group::Bracket, '[', ']'),
|
NodeKind::LeftBracket => math_group(p, Group::Bracket, '[', ']'),
|
||||||
NodeKind::LeftBrace => group(p, Group::Brace, '{', '}'),
|
NodeKind::LeftBrace => math_group(p, Group::Brace, '{', '}'),
|
||||||
|
|
||||||
// Alignment indactor.
|
// Alignment indactor.
|
||||||
NodeKind::Amp => align(p),
|
NodeKind::Amp => math_align(p),
|
||||||
|
|
||||||
_ => p.unexpected(),
|
_ => p.unexpected(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse grouped math.
|
fn math_group(p: &mut Parser, group: Group, l: char, r: char) {
|
||||||
fn group(p: &mut Parser, group: Group, l: char, r: char) {
|
|
||||||
p.perform(NodeKind::Math, |p| {
|
p.perform(NodeKind::Math, |p| {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
p.start_group(group);
|
p.start_group(group);
|
||||||
@ -483,15 +469,13 @@ fn group(p: &mut Parser, group: Group, l: char, r: char) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an alignment indicator.
|
fn math_align(p: &mut Parser) {
|
||||||
fn align(p: &mut Parser) {
|
|
||||||
p.perform(NodeKind::Align, |p| {
|
p.perform(NodeKind::Align, |p| {
|
||||||
p.assert(NodeKind::Amp);
|
p.assert(NodeKind::Amp);
|
||||||
while p.eat_if(NodeKind::Amp) {}
|
while p.eat_if(NodeKind::Amp) {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression.
|
|
||||||
fn expr(p: &mut Parser) -> ParseResult {
|
fn expr(p: &mut Parser) -> ParseResult {
|
||||||
expr_prec(p, false, 0)
|
expr_prec(p, false, 0)
|
||||||
}
|
}
|
||||||
@ -571,7 +555,6 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a primary expression.
|
|
||||||
fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
||||||
if literal(p) {
|
if literal(p) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -599,18 +582,17 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
Some(NodeKind::LeftBracket) => Ok(content_block(p)),
|
Some(NodeKind::LeftBracket) => Ok(content_block(p)),
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
Some(NodeKind::Let) => let_expr(p),
|
Some(NodeKind::Let) => let_binding(p),
|
||||||
Some(NodeKind::Set) => set_expr(p),
|
Some(NodeKind::Set) => set_rule(p),
|
||||||
Some(NodeKind::Show) => show_expr(p),
|
Some(NodeKind::Show) => show_rule(p),
|
||||||
Some(NodeKind::Wrap) => wrap_expr(p),
|
Some(NodeKind::If) => conditional(p),
|
||||||
Some(NodeKind::If) => if_expr(p),
|
Some(NodeKind::While) => while_loop(p),
|
||||||
Some(NodeKind::While) => while_expr(p),
|
Some(NodeKind::For) => for_loop(p),
|
||||||
Some(NodeKind::For) => for_expr(p),
|
Some(NodeKind::Import) => module_import(p),
|
||||||
Some(NodeKind::Import) => import_expr(p),
|
Some(NodeKind::Include) => module_include(p),
|
||||||
Some(NodeKind::Include) => include_expr(p),
|
Some(NodeKind::Break) => break_stmt(p),
|
||||||
Some(NodeKind::Break) => break_expr(p),
|
Some(NodeKind::Continue) => continue_stmt(p),
|
||||||
Some(NodeKind::Continue) => continue_expr(p),
|
Some(NodeKind::Return) => return_stmt(p),
|
||||||
Some(NodeKind::Return) => return_expr(p),
|
|
||||||
|
|
||||||
Some(NodeKind::Error(_, _)) => {
|
Some(NodeKind::Error(_, _)) => {
|
||||||
p.eat();
|
p.eat();
|
||||||
@ -625,10 +607,8 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a literal.
|
|
||||||
fn literal(p: &mut Parser) -> bool {
|
fn literal(p: &mut Parser) -> bool {
|
||||||
match p.peek() {
|
match p.peek() {
|
||||||
// Basic values.
|
|
||||||
Some(
|
Some(
|
||||||
NodeKind::None
|
NodeKind::None
|
||||||
| NodeKind::Auto
|
| NodeKind::Auto
|
||||||
@ -645,7 +625,6 @@ fn literal(p: &mut Parser) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an identifier.
|
|
||||||
fn ident(p: &mut Parser) -> ParseResult {
|
fn ident(p: &mut Parser) -> ParseResult {
|
||||||
match p.peek() {
|
match p.peek() {
|
||||||
Some(NodeKind::Ident(_)) => {
|
Some(NodeKind::Ident(_)) => {
|
||||||
@ -762,8 +741,6 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
|
|||||||
(kind, items)
|
(kind, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression or a named pair, returning whether it's a spread or a
|
|
||||||
/// named pair.
|
|
||||||
fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
|
fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
if p.eat_if(NodeKind::Dots) {
|
if p.eat_if(NodeKind::Dots) {
|
||||||
@ -806,8 +783,6 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a collection into an array, producing errors for anything other than
|
|
||||||
/// expressions.
|
|
||||||
fn array(p: &mut Parser, marker: Marker) {
|
fn array(p: &mut Parser, marker: Marker) {
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
NodeKind::Named | NodeKind::Keyed => Err("expected expression"),
|
NodeKind::Named | NodeKind::Keyed => Err("expected expression"),
|
||||||
@ -816,8 +791,6 @@ fn array(p: &mut Parser, marker: Marker) {
|
|||||||
marker.end(p, NodeKind::Array);
|
marker.end(p, NodeKind::Array);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a collection into a dictionary, producing errors for anything other
|
|
||||||
/// than named and keyed pairs.
|
|
||||||
fn dict(p: &mut Parser, marker: Marker) {
|
fn dict(p: &mut Parser, marker: Marker) {
|
||||||
let mut used = HashSet::new();
|
let mut used = HashSet::new();
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
@ -838,8 +811,6 @@ fn dict(p: &mut Parser, marker: Marker) {
|
|||||||
marker.end(p, NodeKind::Dict);
|
marker.end(p, NodeKind::Dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a collection into a list of parameters, producing errors for
|
|
||||||
/// anything other than identifiers, spread operations and named pairs.
|
|
||||||
fn params(p: &mut Parser, marker: Marker) {
|
fn params(p: &mut Parser, marker: Marker) {
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
kind if kind.is_paren() => Ok(()),
|
kind if kind.is_paren() => Ok(()),
|
||||||
@ -866,7 +837,6 @@ fn code_block(p: &mut Parser) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse expressions.
|
|
||||||
fn code(p: &mut Parser) {
|
fn code(p: &mut Parser) {
|
||||||
while !p.eof() {
|
while !p.eof() {
|
||||||
p.start_group(Group::Expr);
|
p.start_group(Group::Expr);
|
||||||
@ -880,7 +850,6 @@ fn code(p: &mut Parser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a content block: `[...]`.
|
|
||||||
fn content_block(p: &mut Parser) {
|
fn content_block(p: &mut Parser) {
|
||||||
p.perform(NodeKind::ContentBlock, |p| {
|
p.perform(NodeKind::ContentBlock, |p| {
|
||||||
p.start_group(Group::Bracket);
|
p.start_group(Group::Bracket);
|
||||||
@ -889,7 +858,6 @@ fn content_block(p: &mut Parser) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the arguments to a function call.
|
|
||||||
fn args(p: &mut Parser) -> ParseResult {
|
fn args(p: &mut Parser) -> ParseResult {
|
||||||
match p.peek_direct() {
|
match p.peek_direct() {
|
||||||
Some(NodeKind::LeftParen) => {}
|
Some(NodeKind::LeftParen) => {}
|
||||||
@ -931,8 +899,7 @@ fn args(p: &mut Parser) -> ParseResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a let expression.
|
fn let_binding(p: &mut Parser) -> ParseResult {
|
||||||
fn let_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::LetBinding, |p| {
|
p.perform(NodeKind::LetBinding, |p| {
|
||||||
p.assert(NodeKind::Let);
|
p.assert(NodeKind::Let);
|
||||||
|
|
||||||
@ -965,45 +932,30 @@ fn let_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a set expression.
|
fn set_rule(p: &mut Parser) -> ParseResult {
|
||||||
fn set_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::SetRule, |p| {
|
p.perform(NodeKind::SetRule, |p| {
|
||||||
p.assert(NodeKind::Set);
|
p.assert(NodeKind::Set);
|
||||||
ident(p)?;
|
ident(p)?;
|
||||||
args(p)
|
args(p)?;
|
||||||
})
|
if p.eat_if(NodeKind::If) {
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a show expression.
|
|
||||||
fn show_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::ShowRule, |p| {
|
|
||||||
p.assert(NodeKind::Show);
|
|
||||||
let marker = p.marker();
|
|
||||||
expr(p)?;
|
|
||||||
if p.eat_if(NodeKind::Colon) {
|
|
||||||
marker.filter_children(p, |child| match child.kind() {
|
|
||||||
NodeKind::Ident(_) | NodeKind::Colon => Ok(()),
|
|
||||||
_ => Err("expected identifier"),
|
|
||||||
});
|
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
}
|
}
|
||||||
p.expect(NodeKind::As)?;
|
Ok(())
|
||||||
expr(p)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a wrap expression.
|
fn show_rule(p: &mut Parser) -> ParseResult {
|
||||||
fn wrap_expr(p: &mut Parser) -> ParseResult {
|
p.perform(NodeKind::ShowRule, |p| {
|
||||||
p.perform(NodeKind::WrapRule, |p| {
|
p.assert(NodeKind::Show);
|
||||||
p.assert(NodeKind::Wrap);
|
expr(p)?;
|
||||||
ident(p)?;
|
if p.eat_if(NodeKind::Colon) {
|
||||||
p.expect(NodeKind::In)?;
|
expr(p)?;
|
||||||
expr(p)
|
}
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if-else expresion.
|
fn conditional(p: &mut Parser) -> ParseResult {
|
||||||
fn if_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::Conditional, |p| {
|
p.perform(NodeKind::Conditional, |p| {
|
||||||
p.assert(NodeKind::If);
|
p.assert(NodeKind::If);
|
||||||
|
|
||||||
@ -1012,7 +964,7 @@ fn if_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
if p.eat_if(NodeKind::Else) {
|
if p.eat_if(NodeKind::Else) {
|
||||||
if p.at(NodeKind::If) {
|
if p.at(NodeKind::If) {
|
||||||
if_expr(p)?;
|
conditional(p)?;
|
||||||
} else {
|
} else {
|
||||||
body(p)?;
|
body(p)?;
|
||||||
}
|
}
|
||||||
@ -1022,8 +974,7 @@ fn if_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a while expresion.
|
fn while_loop(p: &mut Parser) -> ParseResult {
|
||||||
fn while_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::WhileLoop, |p| {
|
p.perform(NodeKind::WhileLoop, |p| {
|
||||||
p.assert(NodeKind::While);
|
p.assert(NodeKind::While);
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
@ -1031,8 +982,7 @@ fn while_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for-in expression.
|
fn for_loop(p: &mut Parser) -> ParseResult {
|
||||||
fn for_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::ForLoop, |p| {
|
p.perform(NodeKind::ForLoop, |p| {
|
||||||
p.assert(NodeKind::For);
|
p.assert(NodeKind::For);
|
||||||
for_pattern(p)?;
|
for_pattern(p)?;
|
||||||
@ -1042,7 +992,6 @@ fn for_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for loop pattern.
|
|
||||||
fn for_pattern(p: &mut Parser) -> ParseResult {
|
fn for_pattern(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ForPattern, |p| {
|
p.perform(NodeKind::ForPattern, |p| {
|
||||||
ident(p)?;
|
ident(p)?;
|
||||||
@ -1053,8 +1002,7 @@ fn for_pattern(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an import expression.
|
fn module_import(p: &mut Parser) -> ParseResult {
|
||||||
fn import_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::ModuleImport, |p| {
|
p.perform(NodeKind::ModuleImport, |p| {
|
||||||
p.assert(NodeKind::Import);
|
p.assert(NodeKind::Import);
|
||||||
|
|
||||||
@ -1081,33 +1029,29 @@ fn import_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an include expression.
|
fn module_include(p: &mut Parser) -> ParseResult {
|
||||||
fn include_expr(p: &mut Parser) -> ParseResult {
|
|
||||||
p.perform(NodeKind::ModuleInclude, |p| {
|
p.perform(NodeKind::ModuleInclude, |p| {
|
||||||
p.assert(NodeKind::Include);
|
p.assert(NodeKind::Include);
|
||||||
expr(p)
|
expr(p)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a break expression.
|
fn break_stmt(p: &mut Parser) -> ParseResult {
|
||||||
fn break_expr(p: &mut Parser) -> ParseResult {
|
p.perform(NodeKind::LoopBreak, |p| {
|
||||||
p.perform(NodeKind::BreakStmt, |p| {
|
|
||||||
p.assert(NodeKind::Break);
|
p.assert(NodeKind::Break);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a continue expression.
|
fn continue_stmt(p: &mut Parser) -> ParseResult {
|
||||||
fn continue_expr(p: &mut Parser) -> ParseResult {
|
p.perform(NodeKind::LoopContinue, |p| {
|
||||||
p.perform(NodeKind::ContinueStmt, |p| {
|
|
||||||
p.assert(NodeKind::Continue);
|
p.assert(NodeKind::Continue);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a return expression.
|
fn return_stmt(p: &mut Parser) -> ParseResult {
|
||||||
fn return_expr(p: &mut Parser) -> ParseResult {
|
p.perform(NodeKind::FuncReturn, |p| {
|
||||||
p.perform(NodeKind::ReturnStmt, |p| {
|
|
||||||
p.assert(NodeKind::Return);
|
p.assert(NodeKind::Return);
|
||||||
if !p.at(NodeKind::Comma) && !p.eof() {
|
if !p.at(NodeKind::Comma) && !p.eof() {
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
@ -1116,7 +1060,6 @@ fn return_expr(p: &mut Parser) -> ParseResult {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a control flow body.
|
|
||||||
fn body(p: &mut Parser) -> ParseResult {
|
fn body(p: &mut Parser) -> ParseResult {
|
||||||
match p.peek() {
|
match p.peek() {
|
||||||
Some(NodeKind::LeftBracket) => Ok(content_block(p)),
|
Some(NodeKind::LeftBracket) => Ok(content_block(p)),
|
||||||
|
@ -613,12 +613,10 @@ fn keyword(ident: &str) -> Option<NodeKind> {
|
|||||||
"let" => NodeKind::Let,
|
"let" => NodeKind::Let,
|
||||||
"set" => NodeKind::Set,
|
"set" => NodeKind::Set,
|
||||||
"show" => NodeKind::Show,
|
"show" => NodeKind::Show,
|
||||||
"wrap" => NodeKind::Wrap,
|
|
||||||
"if" => NodeKind::If,
|
"if" => NodeKind::If,
|
||||||
"else" => NodeKind::Else,
|
"else" => NodeKind::Else,
|
||||||
"for" => NodeKind::For,
|
"for" => NodeKind::For,
|
||||||
"in" => NodeKind::In,
|
"in" => NodeKind::In,
|
||||||
"as" => NodeKind::As,
|
|
||||||
"while" => NodeKind::While,
|
"while" => NodeKind::While,
|
||||||
"break" => NodeKind::Break,
|
"break" => NodeKind::Break,
|
||||||
"continue" => NodeKind::Continue,
|
"continue" => NodeKind::Continue,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 40 KiB |
@ -38,7 +38,7 @@ fn main() {
|
|||||||
// cache, a deterministic order is important for reproducibility.
|
// cache, a deterministic order is important for reproducibility.
|
||||||
for entry in WalkDir::new("typ").sort_by_file_name() {
|
for entry in WalkDir::new("typ").sort_by_file_name() {
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
if entry.depth() <= 1 {
|
if entry.depth() == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
#set raw(around: none)
|
#set raw(around: none)
|
||||||
#show it: raw as text("IBM Plex Sans", eval(it.text))
|
#show raw: it => text("IBM Plex Sans", eval(it.text))
|
||||||
|
|
||||||
Interacting
|
Interacting
|
||||||
```
|
```
|
||||||
@ -31,19 +31,18 @@ Blue #move(dy: -0.15em)[🌊]
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Error: 23-30 cannot access file system from here
|
// Error: 23-30 cannot access file system from here
|
||||||
#show it: raw as eval(it.text)
|
#show raw: it => eval(it.text)
|
||||||
|
|
||||||
```
|
```
|
||||||
#show strong as image("/res/tiger.jpg")
|
#image("/res/tiger.jpg")
|
||||||
*No absolute tiger!*
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 23-30 cannot access file system from here
|
// Error: 23-30 cannot access file system from here
|
||||||
#show it: raw as eval(it.text)
|
#show raw: it => eval(it.text)
|
||||||
|
|
||||||
```
|
```
|
||||||
#show emph as image("../../res/giraffe.jpg")
|
#show emph: _ => image("../../res/giraffe.jpg")
|
||||||
_No relative giraffe!_
|
_No relative giraffe!_
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
// Everything should be in smallcaps.
|
// Everything should be in smallcaps.
|
||||||
#for color in (red, blue, green, yellow) [
|
#for color in (red, blue, green, yellow) [
|
||||||
#set text("Roboto")
|
#set text("Roboto")
|
||||||
#wrap body in text(fill: color, body)
|
#show it => text(fill: color, it)
|
||||||
#smallcaps(if color != green [
|
#smallcaps(if color != green [
|
||||||
Some
|
Some
|
||||||
] else [
|
] else [
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test field on node.
|
// Test field on node.
|
||||||
#show node: list as {
|
#show list: node => {
|
||||||
test(node.items.len(), 3)
|
test(node.items.len(), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Error: 29-32 unknown field "fun"
|
// Error: 29-32 unknown field "fun"
|
||||||
#show node: heading as node.fun
|
#show heading: node => node.fun
|
||||||
= A
|
= A
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Test import statements.
|
// Test module imports.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test importing semantics.
|
// Test importing semantics.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Test include statements.
|
// Test module includes.
|
||||||
|
|
||||||
---
|
---
|
||||||
#set page(width: 200pt)
|
#set page(width: 200pt)
|
||||||
|
@ -47,10 +47,10 @@
|
|||||||
---
|
---
|
||||||
// Outset padding.
|
// Outset padding.
|
||||||
#set raw(lang: "rust")
|
#set raw(lang: "rust")
|
||||||
#show node: raw as [
|
#show raw: it => [
|
||||||
#set text(8pt)
|
#set text(8pt)
|
||||||
#h(5.6pt, weak: true)
|
#h(5.6pt, weak: true)
|
||||||
#rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), node)
|
#rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), it)
|
||||||
#h(5.6pt, weak: true)
|
#h(5.6pt, weak: true)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ No: list \
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test grid like show rule.
|
// Test grid like show rule.
|
||||||
#show it: desc as table(
|
#show desc: it => table(
|
||||||
columns: 2,
|
columns: 2,
|
||||||
padding: 3pt,
|
padding: 3pt,
|
||||||
..it.items.map(item => (emph(item.term), item.body)).flatten(),
|
..it.items.map(item => (emph(item.term), item.body)).flatten(),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Test headings.
|
// Test headings.
|
||||||
|
|
||||||
---
|
---
|
||||||
#show node: heading as text(blue, node.body)
|
#show heading: it => text(blue, it.body)
|
||||||
|
|
||||||
=
|
=
|
||||||
No heading
|
No heading
|
||||||
@ -46,8 +46,8 @@ multiline.
|
|||||||
= Heading
|
= Heading
|
||||||
|
|
||||||
#set heading(family: "Roboto", fill: eastern)
|
#set heading(family: "Roboto", fill: eastern)
|
||||||
#show it: heading as it.body
|
#show heading: it => it.body
|
||||||
#show it: strong as it.body + [!]
|
#show strong: it => it.body + [!]
|
||||||
|
|
||||||
===== Heading 🌍
|
===== Heading 🌍
|
||||||
#heading(level: 5)[Heading]
|
#heading(level: 5)[Heading]
|
||||||
|
@ -48,6 +48,19 @@ Hello *{x}*
|
|||||||
+ Rhino
|
+ Rhino
|
||||||
+ Tiger
|
+ Tiger
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test conditional set.
|
||||||
|
#show ref: it => {
|
||||||
|
set text(red) if it.target == "unknown"
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
@hello from the @unknown
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 19-24 expected boolean, found integer
|
||||||
|
#set text(red) if 1 + 2
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 11-25 set is only allowed directly in code and content blocks
|
// Error: 11-25 set is only allowed directly in code and content blocks
|
||||||
{ let x = set text(blue) }
|
{ let x = set text(blue) }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Test wrap.
|
// Test bare show without pattern.
|
||||||
|
|
||||||
---
|
---
|
||||||
#set page(height: 130pt)
|
#set page(height: 130pt)
|
||||||
@ -9,28 +9,25 @@
|
|||||||
T. Ypst
|
T. Ypst
|
||||||
]
|
]
|
||||||
|
|
||||||
#wrap body in columns(2, body)
|
#show columns.with(2)
|
||||||
Great typography is at the essence of great storytelling. It is the medium that
|
Great typography is at the essence of great storytelling. It is the medium that
|
||||||
transports meaning from parchment to reader, the wave that sparks a flame
|
transports meaning from parchment to reader, the wave that sparks a flame
|
||||||
in booklovers and the great fulfiller of human need.
|
in booklovers and the great fulfiller of human need.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test wrap in content block.
|
// Test bare show in content block.
|
||||||
A [_B #wrap c in [*#c*]; C_] D
|
A [_B #show c => [*#c*]; C_] D
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test wrap style precedence.
|
// Test style precedence.
|
||||||
#set text(fill: eastern, size: 1.5em)
|
#set text(fill: eastern, size: 1.5em)
|
||||||
#wrap body in text(fill: forest, body)
|
#show text.with(fill: forest)
|
||||||
Forest
|
Forest
|
||||||
|
|
||||||
---
|
---
|
||||||
// Ok, whatever.
|
#show [Shown]
|
||||||
{
|
Ignored
|
||||||
wrap body in 2 * body
|
|
||||||
2
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 4-18 wrap is only allowed directly in code and content blocks
|
// Error: 4-18 show is only allowed directly in code and content blocks
|
||||||
{ (wrap body in 2) * body }
|
{ (show body => 2) * body }
|
@ -3,7 +3,7 @@
|
|||||||
---
|
---
|
||||||
// Override lists.
|
// Override lists.
|
||||||
#set list(around: none)
|
#set list(around: none)
|
||||||
#show v: list as "(" + v.items.join(", ") + ")"
|
#show list: it => "(" + it.items.join(", ") + ")"
|
||||||
|
|
||||||
- A
|
- A
|
||||||
- B
|
- B
|
||||||
@ -14,12 +14,12 @@
|
|||||||
---
|
---
|
||||||
// Test full reset.
|
// Test full reset.
|
||||||
#set heading(size: 1em, strong: false, around: none)
|
#set heading(size: 1em, strong: false, around: none)
|
||||||
#show heading as [B]
|
#show heading: [B]
|
||||||
A [= Heading] C
|
A [= Heading] C
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test full removal.
|
// Test full removal.
|
||||||
#show heading as []
|
#show heading: none
|
||||||
#set heading(around: none)
|
#set heading(around: none)
|
||||||
|
|
||||||
Where is
|
Where is
|
||||||
@ -29,13 +29,13 @@ my heading?
|
|||||||
---
|
---
|
||||||
// Test integrated example.
|
// Test integrated example.
|
||||||
#set heading(size: 1em)
|
#set heading(size: 1em)
|
||||||
#show node: heading as {
|
#show heading: it => {
|
||||||
move(dy: -1pt)[📖]
|
move(dy: -1pt)[📖]
|
||||||
h(5pt)
|
h(5pt)
|
||||||
if node.level == 1 {
|
if it.level == 1 {
|
||||||
underline(text(1.25em, blue, node.body))
|
underline(text(1.25em, blue, it.body))
|
||||||
} else {
|
} else {
|
||||||
text(red, node.body)
|
text(red, it.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +50,10 @@ Another text.
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test set and show in code blocks.
|
// Test set and show in code blocks.
|
||||||
#show node: heading as {
|
#show heading: it => {
|
||||||
set text(red)
|
set text(red)
|
||||||
show "ding" as [🛎]
|
show "ding": [🛎]
|
||||||
node.body
|
it.body
|
||||||
}
|
}
|
||||||
|
|
||||||
= Heading
|
= Heading
|
||||||
@ -62,12 +62,12 @@ Another text.
|
|||||||
// Test that scoping works as expected.
|
// Test that scoping works as expected.
|
||||||
{
|
{
|
||||||
let world = [ World ]
|
let world = [ World ]
|
||||||
show c: "W" as strong(c)
|
show "W": strong
|
||||||
world
|
world
|
||||||
{
|
{
|
||||||
set text(blue)
|
set text(blue)
|
||||||
wrap it in {
|
show it => {
|
||||||
show "o" as "Ø"
|
show "o": "Ø"
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
world
|
world
|
||||||
@ -76,22 +76,27 @@ Another text.
|
|||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
#show heading as 1234
|
#show heading: [1234]
|
||||||
= Heading
|
= Heading
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 25-29 unknown field "page"
|
// Error: 25-29 unknown field "page"
|
||||||
#show it: heading as it.page
|
#show heading: it => it.page
|
||||||
= Heading
|
= Heading
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 10-15 this function cannot be customized with show
|
// Error: 7-12 this function cannot be customized with show
|
||||||
#show _: upper as {}
|
#show upper: it => {}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 16-20 expected content or function, found integer
|
||||||
|
#show heading: 1234
|
||||||
|
= Heading
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-10 expected function, string or regular expression, found color
|
// Error: 7-10 expected function, string or regular expression, found color
|
||||||
#show red as []
|
#show red: []
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-27 show is only allowed directly in code and content blocks
|
// Error: 7-25 show is only allowed directly in code and content blocks
|
||||||
{ 1 + show heading as none }
|
{ 1 + show heading: none }
|
||||||
|
@ -2,17 +2,18 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test basic identity.
|
// Test basic identity.
|
||||||
#show it: heading as it
|
#show heading: it => it
|
||||||
= Heading
|
= Heading
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test more recipes down the chain.
|
// Test more recipes down the chain.
|
||||||
#show it: list as scale(origin: left, x: 80%, it)
|
#show list: scale.with(origin: left, x: 80%)
|
||||||
#show heading as []
|
#show heading: []
|
||||||
#show enum as []
|
#show enum: []
|
||||||
- Actual
|
- Actual
|
||||||
- Tight
|
- Tight
|
||||||
- List
|
- List
|
||||||
|
= Nope
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test recursive base recipe. (Burn it with fire!)
|
// Test recursive base recipe. (Burn it with fire!)
|
||||||
@ -23,11 +24,11 @@
|
|||||||
---
|
---
|
||||||
// Test show rule in function.
|
// Test show rule in function.
|
||||||
#let starwars(body) = [
|
#let starwars(body) = [
|
||||||
#show v: list as {
|
#show list: it => {
|
||||||
stack(dir: ltr,
|
stack(dir: ltr,
|
||||||
text(red, v),
|
text(red, it),
|
||||||
1fr,
|
1fr,
|
||||||
scale(x: -100%, text(blue, v)),
|
scale(x: -100%, text(blue, it)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#body
|
#body
|
||||||
@ -44,8 +45,8 @@
|
|||||||
---
|
---
|
||||||
// Test multi-recursion with nested lists.
|
// Test multi-recursion with nested lists.
|
||||||
#set rect(inset: 2pt)
|
#set rect(inset: 2pt)
|
||||||
#show v: list as rect(stroke: blue, v)
|
#show list: rect.with(stroke: blue)
|
||||||
#show v: list as rect(stroke: red, v)
|
#show list: rect.with(stroke: red)
|
||||||
|
|
||||||
- List
|
- List
|
||||||
- Nested
|
- Nested
|
||||||
@ -55,8 +56,8 @@
|
|||||||
---
|
---
|
||||||
// Inner heading is not finalized. Bug?
|
// Inner heading is not finalized. Bug?
|
||||||
#set heading(around: none)
|
#set heading(around: none)
|
||||||
#show it: heading as it.body
|
#show heading: it => it.body
|
||||||
#show heading as [
|
#show heading: [
|
||||||
= A [
|
= A [
|
||||||
= B
|
= B
|
||||||
]
|
]
|
||||||
|
@ -3,22 +3,22 @@
|
|||||||
---
|
---
|
||||||
// Test classic example.
|
// Test classic example.
|
||||||
#set text("Roboto")
|
#set text("Roboto")
|
||||||
#show phrase: "Der Spiegel" as smallcaps[#phrase]
|
#show "Der Spiegel": smallcaps
|
||||||
Die Zeitung Der Spiegel existiert.
|
Die Zeitung Der Spiegel existiert.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Another classic example.
|
// Another classic example.
|
||||||
#show "TeX" as [T#h(-0.145em)#move(dy: 0.233em)[E]#h(-0.135em)X]
|
#show "TeX": [T#h(-0.145em)#move(dy: 0.233em)[E]#h(-0.135em)X]
|
||||||
#show name: regex("(Lua)?(La)?TeX") as box(text("Latin Modern Roman")[#name])
|
#show regex("(Lua)?(La)?TeX"): name => box(text("Latin Modern Roman")[#name])
|
||||||
|
|
||||||
TeX, LaTeX, LuaTeX and LuaLaTeX!
|
TeX, LaTeX, LuaTeX and LuaLaTeX!
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test out-of-order guarding.
|
// Test out-of-order guarding.
|
||||||
#show "Good" as [Typst!]
|
#show "Good": [Typst!]
|
||||||
#show "Typst" as [Fun!]
|
#show "Typst": [Fun!]
|
||||||
#show "Fun" as [Good!]
|
#show "Fun": [Good!]
|
||||||
#show enum as []
|
#show enum: []
|
||||||
|
|
||||||
Good \
|
Good \
|
||||||
Fun \
|
Fun \
|
||||||
@ -26,32 +26,32 @@ Typst \
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test that replacements happen exactly once.
|
// Test that replacements happen exactly once.
|
||||||
#show "A" as [BB]
|
#show "A": [BB]
|
||||||
#show "B" as [CC]
|
#show "B": [CC]
|
||||||
AA (8)
|
AA (8)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test caseless match and word boundaries.
|
// Test caseless match and word boundaries.
|
||||||
#show regex("(?i)\bworld\b") as [🌍]
|
#show regex("(?i)\bworld\b"): [🌍]
|
||||||
|
|
||||||
Treeworld, the World of worlds, is a world.
|
Treeworld, the World of worlds, is a world.
|
||||||
|
|
||||||
---
|
---
|
||||||
// This is a fun one.
|
// This is a fun one.
|
||||||
#set par(justify: true)
|
#set par(justify: true)
|
||||||
#show letter: regex("\S") as rect(inset: 2pt)[#upper(letter)]
|
#show regex("\S"): letter => rect(inset: 2pt)[#upper(letter)]
|
||||||
#lorem(5)
|
#lorem(5)
|
||||||
|
|
||||||
---
|
---
|
||||||
// See also: https://github.com/mTvare6/hello-world.rs
|
// See also: https://github.com/mTvare6/hello-world.rs
|
||||||
#show it: regex("(?i)rust") as [#it (🚀)]
|
#show regex("(?i)rust"): it => [#it (🚀)]
|
||||||
Rust is memory-safe and blazingly fast. Let's rewrite everything in rust.
|
Rust is memory-safe and blazingly fast. Let's rewrite everything in rust.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Replace worlds but only in lists.
|
// Replace worlds but only in lists.
|
||||||
#show node: list as [
|
#show list: it => [
|
||||||
#show "World" as [🌎]
|
#show "World": [🌎]
|
||||||
#node
|
#it
|
||||||
]
|
]
|
||||||
|
|
||||||
World
|
World
|
||||||
@ -60,6 +60,6 @@ World
|
|||||||
---
|
---
|
||||||
// Test absolute path in layout phase.
|
// Test absolute path in layout phase.
|
||||||
|
|
||||||
#show "GRAPH" as image("/res/graph.png")
|
#show "GRAPH": image("/res/graph.png")
|
||||||
|
|
||||||
The GRAPH has nodes.
|
The GRAPH has nodes.
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#lorem(100)
|
#lorem(100)
|
||||||
|
|
||||||
#let hi = "Hello World"
|
#let hi = "Hello World"
|
||||||
|
#show heading: emph
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
"captures": { "1": { "name": "punctuation.definition.reference.typst" } }
|
"captures": { "1": { "name": "punctuation.definition.reference.typst" } }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"begin": "(#)(let|set|show|wrap|apply|select)\\b",
|
"begin": "(#)(let|set|show)\\b",
|
||||||
"end": "\n|(;)|(?=])",
|
"end": "\n|(;)|(?=])",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": { "name": "keyword.other.typst" },
|
"0": { "name": "keyword.other.typst" },
|
||||||
@ -253,7 +253,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "keyword.other.typst",
|
"name": "keyword.other.typst",
|
||||||
"match": "\\b(let|as|in|from|set|show|wrap|apply|select)\\b"
|
"match": "\\b(let|as|in|from|set|show)\\b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "keyword.control.conditional.typst",
|
"name": "keyword.control.conditional.typst",
|
||||||
@ -277,6 +277,11 @@
|
|||||||
"name": "entity.name.function.typst",
|
"name": "entity.name.function.typst",
|
||||||
"match": "\\b[[:alpha:]_][[:alnum:]_-]*!?(?=\\[|\\()"
|
"match": "\\b[[:alpha:]_][[:alnum:]_-]*!?(?=\\[|\\()"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"comment": "Function name",
|
||||||
|
"name": "entity.name.function.typst",
|
||||||
|
"match": "(?<=\\bshow\\s*)\\b[[:alpha:]_][[:alnum:]_-]*(?=\\s*:)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"comment": "Function arguments",
|
"comment": "Function arguments",
|
||||||
"begin": "(?<=\\b[[:alpha:]_][[:alnum:]_-]*!?)\\(",
|
"begin": "(?<=\\b[[:alpha:]_][[:alnum:]_-]*!?)\\(",
|
||||||
|
@ -38,12 +38,12 @@ function activate(context) {
|
|||||||
|
|
||||||
const rerunCmd = vscode.commands.registerCommand("ShortcutMenuBar.testRerun", () => {
|
const rerunCmd = vscode.commands.registerCommand("ShortcutMenuBar.testRerun", () => {
|
||||||
const uri = vscode.window.activeTextEditor.document.uri
|
const uri = vscode.window.activeTextEditor.document.uri
|
||||||
const components = uri.fsPath.split('tests')
|
const components = uri.fsPath.split(/tests[\/\\]/)
|
||||||
const dir = components[0]
|
const dir = components[0]
|
||||||
const subPath = components[1]
|
const subPath = components[1]
|
||||||
|
|
||||||
cp.exec(
|
cp.exec(
|
||||||
`cargo test --manifest-path ${dir}/Cargo.toml --test typeset ${subPath}`,
|
`cargo test --manifest-path ${dir}/Cargo.toml --all --test tests -- ${subPath}`,
|
||||||
(err, stdout, stderr) => {
|
(err, stdout, stderr) => {
|
||||||
console.log('Ran tests')
|
console.log('Ran tests')
|
||||||
refreshPanel(stdout, stderr)
|
refreshPanel(stdout, stderr)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user