commit
45812b7001
@ -1,7 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{Scope, Scopes, Value};
|
||||
use crate::syntax::visit::{visit_expr, Visit};
|
||||
use crate::syntax::visit::{immutable::visit_expr, Visit};
|
||||
use crate::syntax::{Expr, Ident};
|
||||
|
||||
/// A visitor that captures variable slots.
|
||||
|
@ -72,10 +72,10 @@ impl Exec for syntax::RawNode {
|
||||
ctx.parbreak();
|
||||
}
|
||||
|
||||
let snapshot = ctx.state.clone();
|
||||
let snapshot = Rc::clone(&ctx.state.font);
|
||||
ctx.set_monospace();
|
||||
ctx.push_text(&self.text);
|
||||
ctx.state = snapshot;
|
||||
ctx.state.font = snapshot;
|
||||
|
||||
if self.block {
|
||||
ctx.parbreak();
|
||||
@ -85,16 +85,17 @@ impl Exec for syntax::RawNode {
|
||||
|
||||
impl ExecWithMap for syntax::HeadingNode {
|
||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
ctx.parbreak();
|
||||
|
||||
let snapshot = ctx.state.clone();
|
||||
let font = ctx.state.font_mut();
|
||||
|
||||
let upscale = 1.6 - 0.1 * self.level as f64;
|
||||
font.size *= upscale;
|
||||
font.strong = true;
|
||||
|
||||
self.body.exec_with_map(ctx, map);
|
||||
|
||||
ctx.state = snapshot;
|
||||
|
||||
ctx.parbreak();
|
||||
}
|
||||
}
|
||||
@ -113,8 +114,6 @@ impl ExecWithMap for syntax::EnumItem {
|
||||
}
|
||||
|
||||
fn exec_item(ctx: &mut ExecContext, label: String, body: &syntax::Tree, map: &ExprMap) {
|
||||
ctx.parbreak();
|
||||
|
||||
let label = ctx.exec_stack(|ctx| ctx.push_text(label));
|
||||
let body = ctx.exec_tree_stack(body, map);
|
||||
let stack = StackNode {
|
||||
@ -128,7 +127,6 @@ fn exec_item(ctx: &mut ExecContext, label: String, body: &syntax::Tree, map: &Ex
|
||||
};
|
||||
|
||||
ctx.push_into_stack(stack);
|
||||
ctx.parbreak();
|
||||
}
|
||||
|
||||
impl Exec for Value {
|
||||
@ -172,6 +170,8 @@ impl Exec for TemplateNode {
|
||||
|
||||
impl Exec for TemplateFunc {
|
||||
fn exec(&self, ctx: &mut ExecContext) {
|
||||
let snapshot = ctx.state.clone();
|
||||
self(ctx);
|
||||
ctx.state = snapshot;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
let right = args.named(ctx, "right");
|
||||
let bottom = args.named(ctx, "bottom");
|
||||
let flip = args.named(ctx, "flip");
|
||||
let body = args.eat::<TemplateValue>(ctx);
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
Value::template(move |ctx| {
|
||||
let snapshot = ctx.state.clone();
|
||||
@ -66,13 +66,10 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
|
||||
ctx.pagebreak(false, true, span);
|
||||
body.exec(ctx);
|
||||
|
||||
if let Some(body) = &body {
|
||||
// TODO: Restrict body to a single page?
|
||||
body.exec(ctx);
|
||||
ctx.state = snapshot;
|
||||
ctx.pagebreak(true, false, span);
|
||||
}
|
||||
ctx.state = snapshot;
|
||||
ctx.pagebreak(true, false, span);
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,7 +108,7 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
let second = args.eat::<AlignValue>(ctx);
|
||||
let mut horizontal = args.named::<AlignValue>(ctx, "horizontal");
|
||||
let mut vertical = args.named::<AlignValue>(ctx, "vertical");
|
||||
let body = args.eat::<TemplateValue>(ctx);
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
for value in first.into_iter().chain(second) {
|
||||
match value.axis() {
|
||||
@ -126,23 +123,19 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
|
||||
Value::template(move |ctx| {
|
||||
let snapshot = ctx.state.clone();
|
||||
|
||||
if let Some(horizontal) = horizontal {
|
||||
ctx.state.aligns.cross = horizontal.to_align(ctx.state.lang.dir);
|
||||
}
|
||||
|
||||
if let Some(vertical) = vertical {
|
||||
ctx.state.aligns.main = vertical.to_align(Dir::TTB);
|
||||
if ctx.state.aligns.main != snapshot.aligns.main {
|
||||
let new = vertical.to_align(Dir::TTB);
|
||||
if ctx.state.aligns.main != new {
|
||||
ctx.state.aligns.main = new;
|
||||
ctx.parbreak();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(body) = &body {
|
||||
body.exec(ctx);
|
||||
ctx.state = snapshot;
|
||||
}
|
||||
body.exec(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,9 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
let serif = args.named(ctx, "serif");
|
||||
let sans_serif = args.named(ctx, "sans-serif");
|
||||
let monospace = args.named(ctx, "monospace");
|
||||
let body = args.eat::<TemplateValue>(ctx);
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
Value::template(move |ctx| {
|
||||
let snapshot = ctx.state.clone();
|
||||
let font = ctx.state.font_mut();
|
||||
|
||||
if let Some(linear) = size {
|
||||
@ -67,10 +66,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
font.families_mut().monospace = monospace.clone();
|
||||
}
|
||||
|
||||
if let Some(body) = &body {
|
||||
body.exec(ctx);
|
||||
ctx.state = snapshot;
|
||||
}
|
||||
body.exec(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
@ -161,6 +157,7 @@ pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
let spacing = args.named(ctx, "spacing");
|
||||
let leading = args.named(ctx, "leading");
|
||||
let word_spacing = args.named(ctx, "word-spacing");
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
Value::template(move |ctx| {
|
||||
if let Some(spacing) = spacing {
|
||||
@ -176,6 +173,7 @@ pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
|
||||
ctx.parbreak();
|
||||
body.exec(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
@ -190,6 +188,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
Value::template(move |ctx| {
|
||||
if let Some(dir) = dir.or(iso) {
|
||||
@ -197,6 +196,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
|
||||
ctx.parbreak();
|
||||
body.exec(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ fn line_impl(
|
||||
let position = args.named(ctx, "position");
|
||||
let strength = args.named::<Linear>(ctx, "strength");
|
||||
let extent = args.named(ctx, "extent").unwrap_or_default();
|
||||
let body = args.eat::<TemplateValue>(ctx);
|
||||
let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
|
||||
|
||||
// Suppress any existing strikethrough if strength is explicitly zero.
|
||||
let state = strength.map_or(true, |s| !s.is_zero()).then(|| {
|
||||
@ -245,13 +245,7 @@ fn line_impl(
|
||||
});
|
||||
|
||||
Value::template(move |ctx| {
|
||||
let snapshot = ctx.state.clone();
|
||||
|
||||
*substate(ctx.state.font_mut()) = state.clone();
|
||||
|
||||
if let Some(body) = &body {
|
||||
body.exec(ctx);
|
||||
ctx.state = snapshot;
|
||||
}
|
||||
body.exec(ctx);
|
||||
})
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ pub use tokens::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::diag::Pass;
|
||||
use crate::syntax::visit::{mutable::visit_expr, VisitMut};
|
||||
use crate::syntax::*;
|
||||
|
||||
/// Parse a string of source code.
|
||||
@ -25,7 +26,7 @@ pub fn parse(src: &str) -> Pass<Tree> {
|
||||
|
||||
/// Parse a syntax tree.
|
||||
fn tree(p: &mut Parser) -> Tree {
|
||||
tree_while(p, true, |_| true)
|
||||
tree_while(p, true, &mut |_| true)
|
||||
}
|
||||
|
||||
/// Parse a syntax tree that stays right of the column at the start of the next
|
||||
@ -38,31 +39,70 @@ fn tree_indented(p: &mut Parser) -> Tree {
|
||||
});
|
||||
|
||||
let column = p.column(p.next_start());
|
||||
tree_while(p, false, |p| match p.peek() {
|
||||
tree_while(p, false, &mut |p| match p.peek() {
|
||||
Some(Token::Space(n)) if n >= 1 => p.column(p.next_end()) >= column,
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a syntax tree.
|
||||
fn tree_while(
|
||||
p: &mut Parser,
|
||||
mut at_start: bool,
|
||||
mut f: impl FnMut(&mut Parser) -> bool,
|
||||
) -> Tree {
|
||||
fn tree_while<F>(p: &mut Parser, mut at_start: bool, f: &mut F) -> Tree
|
||||
where
|
||||
F: FnMut(&mut Parser) -> bool,
|
||||
{
|
||||
/// Visitor that adds a recursively parsed rest template to the first wide
|
||||
/// call's argument list and diagnoses all following wide calls.
|
||||
struct WideVisitor<'a, 's, F> {
|
||||
p: &'a mut Parser<'s>,
|
||||
f: &'a mut F,
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'ast, 'a, 's, F> VisitMut<'ast> for WideVisitor<'a, 's, F>
|
||||
where
|
||||
F: FnMut(&mut Parser) -> bool,
|
||||
{
|
||||
fn visit_expr(&mut self, node: &'ast mut Expr) {
|
||||
visit_expr(self, node);
|
||||
|
||||
if let Expr::Call(call) = node {
|
||||
if call.wide {
|
||||
let start = self.p.next_start();
|
||||
let tree = if !self.found {
|
||||
tree_while(self.p, true, self.f)
|
||||
} else {
|
||||
self.p.diag(error!(call.callee.span(), "duplicate wide call"));
|
||||
Tree::default()
|
||||
};
|
||||
|
||||
call.args.items.push(CallArg::Pos(Expr::Template(TemplateExpr {
|
||||
span: self.p.span(start),
|
||||
tree: Rc::new(tree),
|
||||
})));
|
||||
|
||||
self.found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't recurse into templates.
|
||||
fn visit_template(&mut self, _: &'ast mut TemplateExpr) {}
|
||||
}
|
||||
|
||||
// We use `at_start` to keep track of whether we are at the start of a line
|
||||
// or template to know whether things like headings are allowed.
|
||||
let mut tree = vec![];
|
||||
while !p.eof() && f(p) {
|
||||
if let Some(node) = node(p, &mut at_start) {
|
||||
match node {
|
||||
Node::Space => {}
|
||||
Node::Parbreak(_) => {}
|
||||
_ => at_start = false,
|
||||
if let Some(mut node) = node(p, &mut at_start) {
|
||||
at_start &= matches!(node, Node::Space | Node::Parbreak(_));
|
||||
if let Node::Expr(expr) = &mut node {
|
||||
let mut visitor = WideVisitor { p, f, found: false };
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
tree.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
tree
|
||||
}
|
||||
|
||||
@ -236,12 +276,13 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option<Expr> {
|
||||
};
|
||||
|
||||
loop {
|
||||
// Parenthesis or bracket means this is a function call.
|
||||
// Exclamation mark, parenthesis or bracket means this is a function
|
||||
// call.
|
||||
if matches!(
|
||||
p.peek_direct(),
|
||||
Some(Token::LeftParen) | Some(Token::LeftBracket),
|
||||
Some(Token::Excl) | Some(Token::LeftParen) | Some(Token::LeftBracket),
|
||||
) {
|
||||
lhs = call(p, lhs);
|
||||
lhs = call(p, lhs)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -516,7 +557,9 @@ fn block(p: &mut Parser, scoping: bool) -> Expr {
|
||||
}
|
||||
|
||||
/// Parse a function call.
|
||||
fn call(p: &mut Parser, callee: Expr) -> Expr {
|
||||
fn call(p: &mut Parser, callee: Expr) -> Option<Expr> {
|
||||
let wide = p.eat_if(Token::Excl);
|
||||
|
||||
let mut args = match p.peek_direct() {
|
||||
Some(Token::LeftParen) => {
|
||||
p.start_group(Group::Paren, TokenMode::Code);
|
||||
@ -524,10 +567,14 @@ fn call(p: &mut Parser, callee: Expr) -> Expr {
|
||||
p.end_group();
|
||||
args
|
||||
}
|
||||
_ => CallArgs {
|
||||
Some(Token::LeftBracket) => CallArgs {
|
||||
span: Span::at(callee.span().end),
|
||||
items: vec![],
|
||||
},
|
||||
_ => {
|
||||
p.expected_at("argument list", p.prev_end());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if p.peek_direct() == Some(Token::LeftBracket) {
|
||||
@ -535,11 +582,12 @@ fn call(p: &mut Parser, callee: Expr) -> Expr {
|
||||
args.items.push(CallArg::Pos(body));
|
||||
}
|
||||
|
||||
Expr::Call(CallExpr {
|
||||
Some(Expr::Call(CallExpr {
|
||||
span: p.span(callee.span().start),
|
||||
callee: Box::new(callee),
|
||||
wide,
|
||||
args,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parse the arguments to a function call.
|
||||
|
@ -117,7 +117,7 @@ impl<'s> Tokens<'s> {
|
||||
|
||||
// Length two.
|
||||
'=' if self.s.eat_if('=') => Token::EqEq,
|
||||
'!' if self.s.eat_if('=') => Token::BangEq,
|
||||
'!' if self.s.eat_if('=') => Token::ExclEq,
|
||||
'<' if self.s.eat_if('=') => Token::LtEq,
|
||||
'>' if self.s.eat_if('=') => Token::GtEq,
|
||||
'+' if self.s.eat_if('=') => Token::PlusEq,
|
||||
@ -135,6 +135,7 @@ impl<'s> Tokens<'s> {
|
||||
'-' => Token::Hyph,
|
||||
'*' => Token::Star,
|
||||
'/' => Token::Slash,
|
||||
'!' => Token::Excl,
|
||||
'=' => Token::Eq,
|
||||
'<' => Token::Lt,
|
||||
'>' => Token::Gt,
|
||||
@ -750,7 +751,7 @@ mod tests {
|
||||
t!(Code[" a1"]: "/" => Slash);
|
||||
t!(Code: "=" => Eq);
|
||||
t!(Code: "==" => EqEq);
|
||||
t!(Code: "!=" => BangEq);
|
||||
t!(Code: "!=" => ExclEq);
|
||||
t!(Code: "<" => Lt);
|
||||
t!(Code: "<=" => LtEq);
|
||||
t!(Code: ">" => Gt);
|
||||
|
@ -295,7 +295,7 @@ impl BinOp {
|
||||
Token::And => Self::And,
|
||||
Token::Or => Self::Or,
|
||||
Token::EqEq => Self::Eq,
|
||||
Token::BangEq => Self::Neq,
|
||||
Token::ExclEq => Self::Neq,
|
||||
Token::Lt => Self::Lt,
|
||||
Token::LtEq => Self::Leq,
|
||||
Token::Gt => Self::Gt,
|
||||
@ -388,6 +388,8 @@ pub struct CallExpr {
|
||||
pub span: Span,
|
||||
/// The function to call.
|
||||
pub callee: Box<Expr>,
|
||||
/// Whether the call is wide, that is, capturing the template behind it.
|
||||
pub wide: bool,
|
||||
/// The arguments to the function.
|
||||
pub args: CallArgs,
|
||||
}
|
||||
|
@ -42,12 +42,14 @@ pub enum Token<'s> {
|
||||
Hyph,
|
||||
/// A slash: `/`.
|
||||
Slash,
|
||||
/// An exlamation mark.
|
||||
Excl,
|
||||
/// A single equals sign: `=`.
|
||||
Eq,
|
||||
/// Two equals signs: `==`.
|
||||
EqEq,
|
||||
/// An exclamation mark followed by an equals sign: `!=`.
|
||||
BangEq,
|
||||
ExclEq,
|
||||
/// A less-than sign: `<`.
|
||||
Lt,
|
||||
/// A less-than sign followed by an equals sign: `<=`.
|
||||
@ -227,9 +229,10 @@ impl<'s> Token<'s> {
|
||||
Self::Plus => "plus",
|
||||
Self::Hyph => "minus",
|
||||
Self::Slash => "slash",
|
||||
Self::Excl => "exclamation mark",
|
||||
Self::Eq => "assignment operator",
|
||||
Self::EqEq => "equality operator",
|
||||
Self::BangEq => "inequality operator",
|
||||
Self::ExclEq => "inequality operator",
|
||||
Self::Lt => "less-than operator",
|
||||
Self::LtEq => "less-than or equal operator",
|
||||
Self::Gt => "greater-than operator",
|
||||
|
@ -1,54 +1,91 @@
|
||||
//! Syntax tree traversal.
|
||||
//! Mutable and immutable syntax tree traversal.
|
||||
|
||||
use super::*;
|
||||
use crate::syntax::*;
|
||||
|
||||
macro_rules! visit {
|
||||
($(fn $name:ident($v:ident $(, $node:ident: &$ty:ty)?) $body:block)*) => {
|
||||
/// Traverses the syntax tree.
|
||||
pub trait Visit<'ast> {
|
||||
$(fn $name(&mut self $(, $node: &'ast $ty)?) {
|
||||
$name(self, $($node)?);
|
||||
})*
|
||||
/// Implement the immutable and the mutable visitor version.
|
||||
macro_rules! impl_visitors {
|
||||
($($name:ident($($tts:tt)*) $body:block)*) => {
|
||||
macro_rules! r {
|
||||
(rc: $x:expr) => { $x.as_ref() };
|
||||
($x:expr) => { &$x };
|
||||
}
|
||||
|
||||
impl_visitor! {
|
||||
/// Walk syntax trees immutably.
|
||||
Visit,
|
||||
/// Immutable visitor functions.
|
||||
immutable,
|
||||
[$(($name($($tts)*) $body))*]
|
||||
}
|
||||
|
||||
macro_rules! r {
|
||||
(rc: $x:expr) => { std::rc::Rc::make_mut(&mut $x) };
|
||||
($x:expr) => { &mut $x };
|
||||
}
|
||||
|
||||
impl_visitor! {
|
||||
/// Walk syntax trees mutably.
|
||||
VisitMut,
|
||||
/// Mutable visitor functions.
|
||||
mutable,
|
||||
[$(($name($($tts)*) $body mut))*] mut
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement an immutable or mutable visitor.
|
||||
macro_rules! impl_visitor {
|
||||
(
|
||||
#[doc = $visit_doc:expr] $visit:ident,
|
||||
#[doc = $module_doc:expr] $module:ident,
|
||||
[$((
|
||||
$name:ident($v:ident, $node:ident: $ty:ty)
|
||||
$body:block
|
||||
$($fmut:tt)?
|
||||
))*]
|
||||
$($mut:tt)?
|
||||
) => {
|
||||
#[doc = $visit_doc]
|
||||
pub trait $visit<'ast> {
|
||||
/// Visit a definition of a binding.
|
||||
///
|
||||
/// Bindings are, for example, left-hand side of let expressions,
|
||||
/// and key/value patterns in for loops.
|
||||
fn visit_binding(&mut self, _: &'ast Ident) {}
|
||||
fn visit_binding(&mut self, _: &'ast $($mut)? Ident) {}
|
||||
|
||||
/// Visit the entry into a scope.
|
||||
fn visit_enter(&mut self) {}
|
||||
|
||||
/// Visit the exit from a scope.
|
||||
fn visit_exit(&mut self) {}
|
||||
|
||||
$(fn $name(&mut self, $node: &'ast $($fmut)? $ty) {
|
||||
$module::$name(self, $node);
|
||||
})*
|
||||
}
|
||||
|
||||
$(visit! {
|
||||
@$(concat!("Walk a node of type [`", stringify!($ty), "`]."), )?
|
||||
pub fn $name<'ast, V>(
|
||||
#[allow(unused)] $v: &mut V
|
||||
$(, #[allow(unused)] $node: &'ast $ty)?
|
||||
)
|
||||
where
|
||||
V: Visit<'ast> + ?Sized
|
||||
$body
|
||||
})*
|
||||
};
|
||||
|
||||
(@$doc:expr, $($tts:tt)*) => {
|
||||
#[doc = $doc]
|
||||
$($tts)*
|
||||
#[doc = $module_doc]
|
||||
pub mod $module {
|
||||
use super::*;
|
||||
$(
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name<'ast, V>($v: &mut V, $node: &'ast $($fmut)? $ty)
|
||||
where
|
||||
V: $visit<'ast> + ?Sized
|
||||
$body
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
visit! {
|
||||
fn visit_tree(v, node: &Tree) {
|
||||
for node in node {
|
||||
v.visit_node(&node);
|
||||
impl_visitors! {
|
||||
visit_tree(v, tree: Tree) {
|
||||
for node in tree {
|
||||
v.visit_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_node(v, node: &Node) {
|
||||
visit_node(v, node: Node) {
|
||||
match node {
|
||||
Node::Text(_) => {}
|
||||
Node::Space => {}
|
||||
@ -64,20 +101,20 @@ visit! {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_heading(v, node: &HeadingNode) {
|
||||
v.visit_tree(&node.body);
|
||||
visit_heading(v, heading: HeadingNode) {
|
||||
v.visit_tree(r!(rc: heading.body));
|
||||
}
|
||||
|
||||
fn visit_list(v, node: &ListItem) {
|
||||
v.visit_tree(&node.body);
|
||||
visit_list(v, item: ListItem) {
|
||||
v.visit_tree(r!(item.body));
|
||||
}
|
||||
|
||||
fn visit_enum(v, node: &EnumItem) {
|
||||
v.visit_tree(&node.body);
|
||||
visit_enum(v, item: EnumItem) {
|
||||
v.visit_tree(r!(item.body));
|
||||
}
|
||||
|
||||
fn visit_expr(v, node: &Expr) {
|
||||
match node {
|
||||
visit_expr(v, expr: Expr) {
|
||||
match expr {
|
||||
Expr::None(_) => {}
|
||||
Expr::Auto(_) => {}
|
||||
Expr::Bool(_, _) => {}
|
||||
@ -109,121 +146,121 @@ visit! {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_array(v, node: &ArrayExpr) {
|
||||
for expr in &node.items {
|
||||
v.visit_expr(&expr);
|
||||
visit_array(v, array: ArrayExpr) {
|
||||
for expr in r!(array.items) {
|
||||
v.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_dict(v, node: &DictExpr) {
|
||||
for named in &node.items {
|
||||
v.visit_expr(&named.expr);
|
||||
visit_dict(v, dict: DictExpr) {
|
||||
for named in r!(dict.items) {
|
||||
v.visit_expr(r!(named.expr));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_template(v, node: &TemplateExpr) {
|
||||
visit_template(v, template: TemplateExpr) {
|
||||
v.visit_enter();
|
||||
v.visit_tree(&node.tree);
|
||||
v.visit_tree(r!(rc: template.tree));
|
||||
v.visit_exit();
|
||||
}
|
||||
|
||||
fn visit_group(v, node: &GroupExpr) {
|
||||
v.visit_expr(&node.expr);
|
||||
visit_group(v, group: GroupExpr) {
|
||||
v.visit_expr(r!(group.expr));
|
||||
}
|
||||
|
||||
fn visit_block(v, node: &BlockExpr) {
|
||||
if node.scoping {
|
||||
visit_block(v, block: BlockExpr) {
|
||||
if block.scoping {
|
||||
v.visit_enter();
|
||||
}
|
||||
for expr in &node.exprs {
|
||||
v.visit_expr(&expr);
|
||||
for expr in r!(block.exprs) {
|
||||
v.visit_expr(expr);
|
||||
}
|
||||
if node.scoping {
|
||||
if block.scoping {
|
||||
v.visit_exit();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_binary(v, node: &BinaryExpr) {
|
||||
v.visit_expr(&node.lhs);
|
||||
v.visit_expr(&node.rhs);
|
||||
visit_binary(v, binary: BinaryExpr) {
|
||||
v.visit_expr(r!(binary.lhs));
|
||||
v.visit_expr(r!(binary.rhs));
|
||||
}
|
||||
|
||||
fn visit_unary(v, node: &UnaryExpr) {
|
||||
v.visit_expr(&node.expr);
|
||||
visit_unary(v, unary: UnaryExpr) {
|
||||
v.visit_expr(r!(unary.expr));
|
||||
}
|
||||
|
||||
fn visit_call(v, node: &CallExpr) {
|
||||
v.visit_expr(&node.callee);
|
||||
v.visit_args(&node.args);
|
||||
visit_call(v, call: CallExpr) {
|
||||
v.visit_expr(r!(call.callee));
|
||||
v.visit_args(r!(call.args));
|
||||
}
|
||||
|
||||
fn visit_closure(v, node: &ClosureExpr) {
|
||||
for param in node.params.iter() {
|
||||
visit_closure(v, closure: ClosureExpr) {
|
||||
for param in r!(rc: closure.params) {
|
||||
v.visit_binding(param);
|
||||
}
|
||||
v.visit_expr(&node.body);
|
||||
v.visit_expr(r!(rc: closure.body));
|
||||
}
|
||||
|
||||
fn visit_args(v, node: &CallArgs) {
|
||||
for arg in &node.items {
|
||||
visit_args(v, args: CallArgs) {
|
||||
for arg in r!(args.items) {
|
||||
v.visit_arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_arg(v, node: &CallArg) {
|
||||
match node {
|
||||
CallArg::Pos(expr) => v.visit_expr(&expr),
|
||||
CallArg::Named(named) => v.visit_expr(&named.expr),
|
||||
visit_arg(v, arg: CallArg) {
|
||||
match arg {
|
||||
CallArg::Pos(expr) => v.visit_expr(expr),
|
||||
CallArg::Named(named) => v.visit_expr(r!(named.expr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with(v, node: &WithExpr) {
|
||||
v.visit_expr(&node.callee);
|
||||
v.visit_args(&node.args);
|
||||
visit_with(v, with_expr: WithExpr) {
|
||||
v.visit_expr(r!(with_expr.callee));
|
||||
v.visit_args(r!(with_expr.args));
|
||||
}
|
||||
|
||||
fn visit_let(v, node: &LetExpr) {
|
||||
if let Some(init) = &node.init {
|
||||
v.visit_expr(&init);
|
||||
visit_let(v, let_expr: LetExpr) {
|
||||
if let Some(init) = r!(let_expr.init) {
|
||||
v.visit_expr(init);
|
||||
}
|
||||
v.visit_binding(&node.binding);
|
||||
v.visit_binding(r!(let_expr.binding));
|
||||
}
|
||||
|
||||
fn visit_if(v, node: &IfExpr) {
|
||||
v.visit_expr(&node.condition);
|
||||
v.visit_expr(&node.if_body);
|
||||
if let Some(body) = &node.else_body {
|
||||
v.visit_expr(&body);
|
||||
visit_if(v, if_expr: IfExpr) {
|
||||
v.visit_expr(r!(if_expr.condition));
|
||||
v.visit_expr(r!(if_expr.if_body));
|
||||
if let Some(body) = r!(if_expr.else_body) {
|
||||
v.visit_expr(body);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_while(v, node: &WhileExpr) {
|
||||
v.visit_expr(&node.condition);
|
||||
v.visit_expr(&node.body);
|
||||
visit_while(v, while_expr: WhileExpr) {
|
||||
v.visit_expr(r!(while_expr.condition));
|
||||
v.visit_expr(r!(while_expr.body));
|
||||
}
|
||||
|
||||
fn visit_for(v, node: &ForExpr) {
|
||||
v.visit_expr(&node.iter);
|
||||
match &node.pattern {
|
||||
visit_for(v, for_expr: ForExpr) {
|
||||
v.visit_expr(r!(for_expr.iter));
|
||||
match r!(for_expr.pattern) {
|
||||
ForPattern::Value(value) => v.visit_binding(value),
|
||||
ForPattern::KeyValue(key, value) => {
|
||||
v.visit_binding(key);
|
||||
v.visit_binding(value);
|
||||
}
|
||||
}
|
||||
v.visit_expr(&node.body);
|
||||
v.visit_expr(r!(for_expr.body));
|
||||
}
|
||||
|
||||
fn visit_import(v, node: &ImportExpr) {
|
||||
v.visit_expr(&node.path);
|
||||
if let Imports::Idents(idents) = &node.imports {
|
||||
visit_import(v, import_expr: ImportExpr) {
|
||||
v.visit_expr(r!(import_expr.path));
|
||||
if let Imports::Idents(idents) = r!(import_expr.imports) {
|
||||
for ident in idents {
|
||||
v.visit_binding(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_include(v, node: &IncludeExpr) {
|
||||
v.visit_expr(&node.path);
|
||||
visit_include(v, include_expr: IncludeExpr) {
|
||||
v.visit_expr(r!(include_expr.path));
|
||||
}
|
||||
}
|
||||
|
BIN
tests/ref/code/call-wide.png
Normal file
BIN
tests/ref/code/call-wide.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
47
tests/typ/code/call-wide.typ
Normal file
47
tests/typ/code/call-wide.typ
Normal file
@ -0,0 +1,47 @@
|
||||
// Test wide calls.
|
||||
|
||||
---
|
||||
// Test multiple wide calls in separate expressions.
|
||||
#font!(color: eastern) - First
|
||||
#font!(color: forest) - Second
|
||||
|
||||
---
|
||||
// Test in heading.
|
||||
# A #align!(right) B
|
||||
C
|
||||
|
||||
---
|
||||
// Test evaluation semantics.
|
||||
// Ref: false
|
||||
|
||||
#let r
|
||||
#let x = 1
|
||||
#let f(x, body) = (x, body)
|
||||
|
||||
[
|
||||
{ r = f!(x) }
|
||||
{ x = 2 }
|
||||
]
|
||||
|
||||
#test(repr(r), "(1, <template>)")
|
||||
|
||||
---
|
||||
// Test multiple wide calls in one expression.
|
||||
// Ref: false
|
||||
|
||||
#let id(x) = x
|
||||
#let add(x, y) = x + y
|
||||
|
||||
// Error: 11-13 duplicate wide call
|
||||
[{id!() + id!()}]
|
||||
|
||||
// Test nested wide calls.
|
||||
// Error: 2-6 duplicate wide call
|
||||
[#add!(id!())]
|
||||
|
||||
---
|
||||
// Test missing parentheses.
|
||||
// Ref: false
|
||||
|
||||
// Error: 4 expected argument list
|
||||
#f!
|
@ -23,9 +23,6 @@
|
||||
#let alias = type
|
||||
#test(alias(alias), "function")
|
||||
|
||||
// Library function `font` returns template.
|
||||
#test(type(font(size: 12pt)), "template")
|
||||
|
||||
---
|
||||
// Callee expressions.
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Configuration with `page` and `font` functions.
|
||||
#page(width: 450pt, margins: 1cm)
|
||||
#page!(width: 450pt, margins: 1cm)
|
||||
|
||||
// There are variables and they can take normal values like strings, ...
|
||||
#let city = "Berlin"
|
||||
|
@ -4,18 +4,24 @@
|
||||
// Test auto sizing.
|
||||
|
||||
Auto-sized circle. \
|
||||
#circle(fill: #eb5278, align(center, center, [But, soft!]))
|
||||
#circle(fill: #eb5278)[
|
||||
#align!(center, center)
|
||||
But, soft!
|
||||
]
|
||||
|
||||
Center-aligned rect in auto-sized circle.
|
||||
#circle(fill: forest)[
|
||||
#align(center, center)
|
||||
#rect(fill: conifer, pad(5pt)[But, soft!])
|
||||
#align!(center, center)
|
||||
#rect!(fill: conifer)
|
||||
#pad!(5pt)
|
||||
But, soft!
|
||||
]
|
||||
|
||||
100%-width rect in auto-sized circle. \
|
||||
#circle(fill: forest, rect(width: 100%, fill: conifer)[
|
||||
#circle(fill: forest)[
|
||||
#rect!(width: 100%, fill: conifer)
|
||||
But, soft! what light through yonder window breaks?
|
||||
])
|
||||
]
|
||||
|
||||
Expanded by height.
|
||||
#circle(fill: conifer)[A \ B \ C]
|
||||
@ -23,8 +29,8 @@ Expanded by height.
|
||||
---
|
||||
// Test relative sizing.
|
||||
#rect(width: 100%, height: 50pt, fill: #aaa)[
|
||||
#align(center, center)
|
||||
#font(color: #fff)
|
||||
#align!(center, center)
|
||||
#font!(color: #fff)
|
||||
#circle(radius: 10pt, fill: eastern)[A]
|
||||
#circle(height: 60%, fill: eastern)[B]
|
||||
#circle(width: 20% + 20pt, fill: eastern)[C]
|
||||
|
@ -3,11 +3,10 @@
|
||||
---
|
||||
100% rect in 100% ellipse in fixed rect. \
|
||||
#rect(width: 3cm, height: 2cm, fill: #2a631a)[
|
||||
#ellipse(width: 100%, height: 100%, fill: forest)[
|
||||
#rect(width: 100%, height: 100%, fill: conifer)[
|
||||
#align(center, center)[Stuff inside an ellipse!]
|
||||
]
|
||||
]
|
||||
#ellipse!(width: 100%, height: 100%, fill: forest)
|
||||
#rect!(width: 100%, height: 100%, fill: conifer)
|
||||
#align!(center, center)
|
||||
Stuff inside an ellipse!
|
||||
]
|
||||
|
||||
Auto-sized ellipse. \
|
||||
|
@ -3,7 +3,7 @@
|
||||
---
|
||||
// Test the `rect` function.
|
||||
|
||||
#page(width: 150pt)
|
||||
#page!(width: 150pt)
|
||||
|
||||
// Fit to text.
|
||||
#rect(fill: conifer)[Textbox]
|
||||
|
@ -3,11 +3,10 @@
|
||||
---
|
||||
Auto-sized square. \
|
||||
#square(fill: eastern)[
|
||||
#align(center)
|
||||
#pad(5pt)[
|
||||
#font(color: #fff, weight: bold)
|
||||
Typst
|
||||
]
|
||||
#align!(center)
|
||||
#pad!(5pt)
|
||||
#font!(color: #fff, weight: bold)
|
||||
Typst
|
||||
]
|
||||
|
||||
---
|
||||
@ -18,14 +17,14 @@ Auto-sized square. \
|
||||
|
||||
---
|
||||
// Test height overflow.
|
||||
#page(width: 75pt, height: 100pt)
|
||||
#page!(width: 75pt, height: 100pt)
|
||||
#square(fill: conifer)[
|
||||
But, soft! what light through yonder window breaks?
|
||||
]
|
||||
|
||||
---
|
||||
// Test width overflow.
|
||||
#page(width: 100pt, height: 75pt)
|
||||
#page!(width: 100pt, height: 75pt)
|
||||
#square(fill: conifer)[
|
||||
But, soft! what light through yonder window breaks?
|
||||
]
|
||||
|
@ -12,7 +12,7 @@ Apart
|
||||
---
|
||||
// Test block over multiple pages.
|
||||
|
||||
#page(height: 60pt)
|
||||
#page!(height: 60pt)
|
||||
|
||||
First!
|
||||
#block[
|
||||
|
@ -3,7 +3,7 @@
|
||||
---
|
||||
#let rect(width, color) = rect(width: width, height: 2cm, fill: color)
|
||||
|
||||
#page(width: 100pt, height: 140pt)
|
||||
#page!(width: 100pt, height: 140pt)
|
||||
#grid(
|
||||
columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%),
|
||||
rect(0.5cm, #2a631a),
|
||||
@ -33,7 +33,7 @@
|
||||
)
|
||||
|
||||
---
|
||||
#page(height: 3cm, width: 2cm)
|
||||
#page!(height: 3cm, width: 2cm)
|
||||
#grid(
|
||||
columns: (1fr, 1cm, 1fr, 1fr),
|
||||
column-dir: ttb,
|
||||
@ -46,8 +46,8 @@
|
||||
)
|
||||
|
||||
---
|
||||
#page(height: 3cm, margins: 0pt)
|
||||
#align(center)
|
||||
#page!(height: 3cm, margins: 0pt)
|
||||
#align!(center)
|
||||
#grid(
|
||||
columns: (1fr,),
|
||||
rows: (1fr, auto, 2fr),
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test using the `grid` function to create a finance table.
|
||||
|
||||
---
|
||||
#page(width: 12cm, height: 2.5cm)
|
||||
#page!(width: 12cm, height: 2.5cm)
|
||||
#grid(
|
||||
columns: 5,
|
||||
gutter-columns: (2fr, 1fr, 1fr),
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test grid cells that overflow to the next region.
|
||||
|
||||
---
|
||||
#page(width: 5cm, height: 3cm)
|
||||
#page!(width: 5cm, height: 3cm)
|
||||
#grid(
|
||||
columns: 2,
|
||||
gutter-rows: 3 * (8pt,),
|
||||
@ -18,7 +18,7 @@
|
||||
---
|
||||
// Test a column that starts overflowing right after another row/column did
|
||||
// that.
|
||||
#page(width: 5cm, height: 2cm)
|
||||
#page!(width: 5cm, height: 2cm)
|
||||
#grid(
|
||||
columns: 4 * (1fr,),
|
||||
gutter-rows: (10pt,),
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
---
|
||||
// Test two columns in the same row overflowing by a different amount.
|
||||
#page(width: 5cm, height: 2cm)
|
||||
#page!(width: 5cm, height: 2cm)
|
||||
#grid(
|
||||
columns: 3 * (1fr,),
|
||||
gutter-rows: (8pt,),
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
---
|
||||
// Test grid within a grid, overflowing.
|
||||
#page(width: 5cm, height: 2.25cm)
|
||||
#page!(width: 5cm, height: 2.25cm)
|
||||
#grid(
|
||||
columns: 4 * (1fr,),
|
||||
gutter-rows: (10pt,),
|
||||
@ -62,7 +62,7 @@
|
||||
|
||||
---
|
||||
// Test partition of `fr` units before and after multi-region layout.
|
||||
#page(width: 5cm, height: 4cm)
|
||||
#page!(width: 5cm, height: 4cm)
|
||||
#grid(
|
||||
columns: 2 * (1fr,),
|
||||
rows: (1fr, 2fr, auto, 1fr, 1cm),
|
||||
|
@ -6,9 +6,8 @@
|
||||
|
||||
// All sides together.
|
||||
#rect(fill: conifer)[
|
||||
#pad(10pt, right: 20pt)[
|
||||
#rect(width: 20pt, height: 20pt, fill: #eb5278)
|
||||
]
|
||||
#pad!(10pt, right: 20pt)
|
||||
#rect(width: 20pt, height: 20pt, fill: #eb5278)
|
||||
]
|
||||
|
||||
// Error: 13-23 missing argument: body
|
||||
@ -29,7 +28,7 @@ Hi #box(pad(left: 10pt)) there
|
||||
---
|
||||
// Test that the pad node doesn't consume the whole region.
|
||||
|
||||
#page(height: 6cm)
|
||||
#page!(height: 6cm)
|
||||
|
||||
#align(left)[Before]
|
||||
#pad(10pt, image("../../res/tiger.jpg"))
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
// Set width and height.
|
||||
#page(width: 120pt, height: 120pt)
|
||||
#page!(width: 120pt, height: 120pt)
|
||||
#page(width: 40pt)[High]
|
||||
#page(height: 40pt)[Wide]
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
]
|
||||
|
||||
// Set individual margins.
|
||||
#page(height: 40pt)
|
||||
#page!(height: 40pt)
|
||||
#page(left: 0pt, align(left)[Left])
|
||||
#page(right: 0pt, align(right)[Right])
|
||||
#page(top: 0pt, align(top)[Top])
|
||||
@ -22,21 +22,21 @@
|
||||
// Ensure that specific margins override general margins.
|
||||
#page(margins: 0pt, left: 20pt)[Overriden]
|
||||
|
||||
// Error: 7-18 unknown variable
|
||||
#page(nonexistant)
|
||||
// Error: 8-19 unknown variable
|
||||
#page!(nonexistant)
|
||||
|
||||
// Flipped predefined paper.
|
||||
#page("a11", flip: true)[Flipped A11]
|
||||
|
||||
// Flipped custom page size.
|
||||
#page(width: 40pt, height: 120pt)
|
||||
#page(flip: true)
|
||||
#page!(width: 40pt, height: 120pt)
|
||||
#page!(flip: true)
|
||||
Wide
|
||||
|
||||
---
|
||||
// Test a combination of pages with bodies and normal content.
|
||||
|
||||
#page(height: 50pt)
|
||||
#page!(height: 50pt)
|
||||
|
||||
#page[First]
|
||||
#page[Second]
|
||||
|
@ -3,7 +3,7 @@
|
||||
---
|
||||
First of two
|
||||
#pagebreak()
|
||||
#page(height: 40pt)
|
||||
#page!(height: 40pt)
|
||||
|
||||
---
|
||||
// Make sure that you can't do page related stuff in a container.
|
||||
@ -14,7 +14,7 @@ A
|
||||
#pagebreak()
|
||||
|
||||
// Error: 11-15 cannot modify page from here
|
||||
#page("a4")
|
||||
#page("a4")[]
|
||||
]
|
||||
C
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test simple text.
|
||||
|
||||
---
|
||||
#page(width: 250pt, height: 110pt)
|
||||
#page!(width: 250pt, height: 110pt)
|
||||
|
||||
But, soft! what light through yonder window breaks? It is the east, and Juliet
|
||||
is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and
|
||||
|
@ -3,54 +3,54 @@
|
||||
---
|
||||
// Test reordering with different top-level paragraph directions.
|
||||
#let text = [Text טֶקסט]
|
||||
#font(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
#lang("he") {text}
|
||||
#lang("de") {text}
|
||||
#font!(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
#lang!("he") {text}
|
||||
#lang!("de") {text}
|
||||
|
||||
---
|
||||
// Test that consecutive, embedded LTR runs stay LTR.
|
||||
// Here, we have two runs: "A" and italic "B".
|
||||
#let text = [أنت A_B_مطرC]
|
||||
#font(family: ("EB Garamond", "Noto Sans Arabic"))
|
||||
#lang("ar") {text}
|
||||
#lang("de") {text}
|
||||
#font!(family: ("EB Garamond", "Noto Sans Arabic"))
|
||||
#lang!("ar") {text}
|
||||
#lang!("de") {text}
|
||||
|
||||
---
|
||||
// Test that consecutive, embedded RTL runs stay RTL.
|
||||
// Here, we have three runs: "גֶ", bold "שֶׁ", and "ם".
|
||||
#let text = [Aגֶ*שֶׁ*םB]
|
||||
#font(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
#lang("he") {text}
|
||||
#lang("de") {text}
|
||||
#font!(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
#lang!("he") {text}
|
||||
#lang!("de") {text}
|
||||
|
||||
---
|
||||
// Test embedding up to level 4 with isolates.
|
||||
#font(family: ("EB Garamond", "Noto Serif Hebrew", "Twitter Color Emoji"))
|
||||
#lang(dir: rtl)
|
||||
#font!(family: ("EB Garamond", "Noto Serif Hebrew", "Twitter Color Emoji"))
|
||||
#lang!(dir: rtl)
|
||||
א\u{2066}A\u{2067}Bב\u{2069}?
|
||||
|
||||
---
|
||||
// Test hard line break (leads to two paragraphs in unicode-bidi).
|
||||
#font(family: ("Noto Sans Arabic", "EB Garamond"))
|
||||
#lang("ar")
|
||||
#font!(family: ("Noto Sans Arabic", "EB Garamond"))
|
||||
#lang!("ar")
|
||||
Life المطر هو الحياة \
|
||||
الحياة تمطر is rain.
|
||||
|
||||
---
|
||||
// Test spacing.
|
||||
#font(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
#font!(family: ("EB Garamond", "Noto Serif Hebrew"))
|
||||
L #h(1cm) ריווחR \
|
||||
Lריווח #h(1cm) R
|
||||
|
||||
---
|
||||
// Test inline object.
|
||||
#font(family: ("Noto Serif Hebrew", "EB Garamond"))
|
||||
#lang("he")
|
||||
#font!(family: ("Noto Serif Hebrew", "EB Garamond"))
|
||||
#lang!("he")
|
||||
קרנפיםRh#image("../../res/rhino.png", height: 11pt)inoחיים
|
||||
|
||||
---
|
||||
// Test the `lang` function.
|
||||
// Ref: false
|
||||
|
||||
// Error: 12-15 must be horizontal
|
||||
#lang(dir: ttb)
|
||||
// Error: 13-16 must be horizontal
|
||||
#lang!(dir: ttb)
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test chinese text from Wikipedia.
|
||||
|
||||
---
|
||||
#font(family: "Noto Serif CJK SC")
|
||||
#font!(family: "Noto Serif CJK SC")
|
||||
|
||||
是美国广播公司电视剧《迷失》第3季的第22和23集,也是全剧的第71集和72集
|
||||
由执行制作人戴蒙·林道夫和卡尔顿·库斯编剧,导演则是另一名执行制作人杰克·本德
|
||||
|
@ -35,9 +35,9 @@ Emoji: 🐪, 🌋, 🏞
|
||||
---
|
||||
// Test top and bottom edge.
|
||||
|
||||
#page(width: 170pt)
|
||||
#page!(width: 170pt)
|
||||
#let try(top, bottom) = rect(fill: conifer)[
|
||||
#font(top-edge: top, bottom-edge: bottom)
|
||||
#font!(top-edge: top, bottom-edge: bottom)
|
||||
`From `#top` to `#bottom
|
||||
]
|
||||
|
||||
@ -48,7 +48,7 @@ Emoji: 🐪, 🌋, 🏞
|
||||
|
||||
---
|
||||
// Test class definitions.
|
||||
#font(sans-serif: "PT Sans")
|
||||
#font!(sans-serif: "PT Sans")
|
||||
#font(family: sans-serif)[Sans-serif.] \
|
||||
#font(family: monospace)[Monospace.] \
|
||||
#font(family: monospace, monospace: ("Nope", "Latin Modern Math"))[Math.]
|
||||
@ -57,18 +57,18 @@ Emoji: 🐪, 🌋, 🏞
|
||||
// Ref: false
|
||||
|
||||
// Error: 7-12 unexpected argument
|
||||
#font(false)
|
||||
#font(false)[]
|
||||
|
||||
// Error: 3:14-3:18 expected font style, found font weight
|
||||
// Error: 2:28-2:34 expected font weight, found string
|
||||
// Error: 1:43-1:44 expected string or array of strings, found integer
|
||||
#font(style: bold, weight: "thin", serif: 0)
|
||||
#font(style: bold, weight: "thin", serif: 0)[]
|
||||
|
||||
// Warning: 15-19 should be between 100 and 900
|
||||
#font(weight: 2700)
|
||||
#font(weight: 2700)[]
|
||||
|
||||
// Warning: 16-21 should be between 50% and 200%
|
||||
#font(stretch: 1000%)
|
||||
#font(stretch: 1000%)[]
|
||||
|
||||
// Error: 7-27 unexpected argument
|
||||
#font(something: "invalid")
|
||||
#font(something: "invalid")[]
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
---
|
||||
// FIXME: Word spacing doesn't work due to new shaping process.
|
||||
#par(spacing: 10pt, leading: 25%, word-spacing: 1pt)
|
||||
#par!(spacing: 10pt, leading: 25%, word-spacing: 1pt)
|
||||
|
||||
But, soft! what light through yonder window breaks? It is the east, and Juliet
|
||||
is the sun.
|
||||
|
||||
---
|
||||
// Test that it finishes an existing paragraph.
|
||||
Hello #par(word-spacing: 0pt) t h e r e !
|
||||
Hello #par!(word-spacing: 0pt) t h e r e !
|
||||
|
@ -7,11 +7,11 @@
|
||||
Le fira
|
||||
|
||||
// This should just shape nicely.
|
||||
#font(family: "Noto Sans Arabic")
|
||||
#font!(family: "Noto Sans Arabic")
|
||||
دع النص يمطر عليك
|
||||
|
||||
// This should form a three-member family.
|
||||
#font(family: "Twitter Color Emoji")
|
||||
#font!(family: "Twitter Color Emoji")
|
||||
👩👩👦 🤚🏿
|
||||
|
||||
// These two shouldn't be affected by a zero-width joiner.
|
||||
@ -20,7 +20,7 @@ Le fira
|
||||
---
|
||||
// Test font fallback.
|
||||
|
||||
#font(family: ("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji"))
|
||||
#font!(family: ("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji"))
|
||||
|
||||
// Font fallback for emoji.
|
||||
A😀B
|
||||
@ -40,6 +40,6 @@ A🐈中文B
|
||||
---
|
||||
// Test reshaping.
|
||||
|
||||
#font(family: "Noto Serif Hebrew")
|
||||
#lang("he")
|
||||
#font!(family: "Noto Serif Hebrew")
|
||||
#lang!("he")
|
||||
ס \ טֶ
|
||||
|
@ -372,10 +372,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
|
||||
let repr = typst::pretty::pretty(args);
|
||||
args.items.clear();
|
||||
Value::template(move |ctx| {
|
||||
let snapshot = ctx.state.clone();
|
||||
ctx.set_monospace();
|
||||
ctx.push_text(&repr);
|
||||
ctx.state = snapshot;
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -171,12 +171,12 @@
|
||||
{
|
||||
"comment": "Function name",
|
||||
"name": "entity.name.function.typst",
|
||||
"match": "((#)[[:alpha:]_][[:alnum:]_-]*)(?=\\[|\\()",
|
||||
"match": "((#)[[:alpha:]_][[:alnum:]_-]*!?)(?=\\[|\\()",
|
||||
"captures": { "2": { "name": "punctuation.definition.function.typst" } }
|
||||
},
|
||||
{
|
||||
"comment": "Function arguments",
|
||||
"begin": "(?<=#[[:alpha:]_][[:alnum:]_-]*)\\(",
|
||||
"begin": "(?<=#[[:alpha:]_][[:alnum:]_-]*!?)\\(",
|
||||
"end": "\\)",
|
||||
"captures": { "0": { "name": "punctuation.definition.group.typst" } },
|
||||
"patterns": [{ "include": "#arguments" }]
|
||||
@ -231,11 +231,11 @@
|
||||
{
|
||||
"comment": "Function name",
|
||||
"name": "entity.name.function.typst",
|
||||
"match": "\\b[[:alpha:]_][[:alnum:]_-]*(?=\\[|\\(|\\s+\\bwith\\b)"
|
||||
"match": "\\b[[:alpha:]_][[:alnum:]_-]*!?(?=(\\[|\\()|\\s+\\bwith\\b)"
|
||||
},
|
||||
{
|
||||
"comment": "Function arguments",
|
||||
"begin": "(?<=\\b[[:alpha:]_][[:alnum:]_-]*|\\bwith\\b\\s+)\\(",
|
||||
"begin": "(?<=\\b[[:alpha:]_][[:alnum:]_-]*!?|\\bwith\\b\\s+)\\(",
|
||||
"end": "\\)",
|
||||
"captures": { "0": { "name": "punctuation.definition.group.typst" } },
|
||||
"patterns": [{ "include": "#arguments" }]
|
||||
|
Loading…
x
Reference in New Issue
Block a user