mirror of
https://github.com/typst/typst
synced 2025-06-28 08:12:53 +08:00
Basic enums
This commit is contained in:
parent
3330767c20
commit
4dbd9285c9
@ -57,10 +57,11 @@ impl ExecWithMap for syntax::Node {
|
|||||||
Self::Parbreak(_) => ctx.parbreak(),
|
Self::Parbreak(_) => ctx.parbreak(),
|
||||||
Self::Strong(_) => ctx.state.font_mut().strong ^= true,
|
Self::Strong(_) => ctx.state.font_mut().strong ^= true,
|
||||||
Self::Emph(_) => ctx.state.font_mut().emph ^= true,
|
Self::Emph(_) => ctx.state.font_mut().emph ^= true,
|
||||||
Self::Raw(raw) => raw.exec(ctx),
|
Self::Raw(n) => n.exec(ctx),
|
||||||
Self::Heading(heading) => heading.exec_with_map(ctx, map),
|
Self::Heading(n) => n.exec_with_map(ctx, map),
|
||||||
Self::List(list) => list.exec_with_map(ctx, map),
|
Self::List(n) => n.exec_with_map(ctx, map),
|
||||||
Self::Expr(expr) => map[&(expr as *const _)].exec(ctx),
|
Self::Enum(n) => n.exec_with_map(ctx, map),
|
||||||
|
Self::Expr(n) => map[&(n as *const _)].exec(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,33 +99,43 @@ impl ExecWithMap for syntax::HeadingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecWithMap for syntax::ListNode {
|
impl ExecWithMap for syntax::ListItem {
|
||||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
ctx.parbreak();
|
exec_item(ctx, "•".to_string(), &self.body, map);
|
||||||
|
|
||||||
let bullet = ctx.exec_stack(|ctx| ctx.push_text("•"));
|
|
||||||
let body = ctx.exec_tree_stack(&self.body, map);
|
|
||||||
|
|
||||||
let stack = StackNode {
|
|
||||||
dirs: Gen::new(Dir::TTB, ctx.state.lang.dir),
|
|
||||||
aspect: None,
|
|
||||||
children: vec![
|
|
||||||
StackChild::Any(bullet.into(), Gen::default()),
|
|
||||||
StackChild::Spacing(ctx.state.font.size / 2.0),
|
|
||||||
StackChild::Any(body.into(), Gen::default()),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.push(FixedNode {
|
|
||||||
width: None,
|
|
||||||
height: None,
|
|
||||||
child: stack.into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.parbreak();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExecWithMap for syntax::EnumItem {
|
||||||
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
|
let label = self.number.unwrap_or(1).to_string() + ".";
|
||||||
|
exec_item(ctx, label, &self.body, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
dirs: Gen::new(Dir::TTB, ctx.state.lang.dir),
|
||||||
|
aspect: None,
|
||||||
|
children: vec![
|
||||||
|
StackChild::Any(label.into(), Gen::default()),
|
||||||
|
StackChild::Spacing(ctx.state.font.size / 2.0),
|
||||||
|
StackChild::Any(body.into(), Gen::default()),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.push(FixedNode {
|
||||||
|
width: None,
|
||||||
|
height: None,
|
||||||
|
child: stack.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.parbreak();
|
||||||
|
}
|
||||||
|
|
||||||
impl Exec for Value {
|
impl Exec for Value {
|
||||||
fn exec(&self, ctx: &mut ExecContext) {
|
fn exec(&self, ctx: &mut ExecContext) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -25,25 +25,33 @@ pub fn parse(src: &str) -> Pass<Tree> {
|
|||||||
|
|
||||||
/// Parse a syntax tree.
|
/// Parse a syntax tree.
|
||||||
fn tree(p: &mut Parser) -> Tree {
|
fn tree(p: &mut Parser) -> Tree {
|
||||||
tree_while(p, |_| true)
|
tree_while(p, true, |_| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a syntax tree that stays right of the column at the start of the next
|
/// Parse a syntax tree that stays right of the column at the start of the next
|
||||||
/// non-whitespace token.
|
/// non-whitespace token.
|
||||||
fn tree_indented(p: &mut Parser) -> Tree {
|
fn tree_indented(p: &mut Parser) -> Tree {
|
||||||
p.skip_white();
|
p.eat_while(|t| match t {
|
||||||
|
Token::Space(n) => n == 0,
|
||||||
|
Token::LineComment(_) | Token::BlockComment(_) => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
let column = p.column(p.next_start());
|
let column = p.column(p.next_start());
|
||||||
tree_while(p, |p| match p.peek() {
|
tree_while(p, false, |p| match p.peek() {
|
||||||
Some(Token::Space(n)) if n >= 1 => p.column(p.next_end()) >= column,
|
Some(Token::Space(n)) if n >= 1 => p.column(p.next_end()) >= column,
|
||||||
_ => true,
|
_ => true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a syntax tree.
|
/// Parse a syntax tree.
|
||||||
fn tree_while(p: &mut Parser, mut f: impl FnMut(&mut Parser) -> bool) -> Tree {
|
fn tree_while(
|
||||||
// We keep track of whether we are at the start of a block or paragraph
|
p: &mut Parser,
|
||||||
// to know whether things like headings are allowed.
|
mut at_start: bool,
|
||||||
let mut at_start = true;
|
mut f: impl FnMut(&mut Parser) -> bool,
|
||||||
|
) -> Tree {
|
||||||
|
// 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![];
|
let mut tree = vec![];
|
||||||
while !p.eof() && f(p) {
|
while !p.eof() && f(p) {
|
||||||
if let Some(node) = node(p, &mut at_start) {
|
if let Some(node) = node(p, &mut at_start) {
|
||||||
@ -85,19 +93,13 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
|||||||
Token::Star => Node::Strong(span),
|
Token::Star => Node::Strong(span),
|
||||||
Token::Underscore => Node::Emph(span),
|
Token::Underscore => Node::Emph(span),
|
||||||
Token::Raw(t) => raw(p, t),
|
Token::Raw(t) => raw(p, t),
|
||||||
Token::Hashtag => {
|
Token::Hashtag if *at_start => return Some(heading(p)),
|
||||||
if *at_start {
|
Token::Hyph if *at_start => return Some(list_item(p)),
|
||||||
return Some(heading(p));
|
Token::Numbering(number) if *at_start => return Some(enum_item(p, number)),
|
||||||
} else {
|
|
||||||
Node::Text(p.peek_src().into())
|
// Line-based markup that is not currently at the start of the line.
|
||||||
}
|
Token::Hashtag | Token::Hyph | Token::Numbering(_) => {
|
||||||
}
|
Node::Text(p.peek_src().into())
|
||||||
Token::Hyph => {
|
|
||||||
if *at_start {
|
|
||||||
return Some(list(p));
|
|
||||||
} else {
|
|
||||||
Node::Text(p.peek_src().into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hashtag + keyword / identifier.
|
// Hashtag + keyword / identifier.
|
||||||
@ -118,19 +120,12 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
|||||||
}
|
}
|
||||||
p.end_group();
|
p.end_group();
|
||||||
|
|
||||||
// Uneat spaces we might have eaten eagerly.
|
|
||||||
return expr.map(Node::Expr);
|
return expr.map(Node::Expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block.
|
// Block and template.
|
||||||
Token::LeftBrace => {
|
Token::LeftBrace => return Some(Node::Expr(block(p, false))),
|
||||||
return Some(Node::Expr(block(p, false)));
|
Token::LeftBracket => return Some(Node::Expr(template(p))),
|
||||||
}
|
|
||||||
|
|
||||||
// Template.
|
|
||||||
Token::LeftBracket => {
|
|
||||||
return Some(Node::Expr(template(p)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comments.
|
// Comments.
|
||||||
Token::LineComment(_) | Token::BlockComment(_) => {
|
Token::LineComment(_) | Token::BlockComment(_) => {
|
||||||
@ -202,11 +197,19 @@ fn heading(p: &mut Parser) -> Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single list item.
|
/// Parse a single list item.
|
||||||
fn list(p: &mut Parser) -> Node {
|
fn list_item(p: &mut Parser) -> Node {
|
||||||
let start = p.next_start();
|
let start = p.next_start();
|
||||||
p.assert(Token::Hyph);
|
p.assert(Token::Hyph);
|
||||||
let body = tree_indented(p);
|
let body = tree_indented(p);
|
||||||
Node::List(ListNode { span: p.span(start), body })
|
Node::List(ListItem { span: p.span(start), body })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a single enum item.
|
||||||
|
fn enum_item(p: &mut Parser, number: Option<usize>) -> Node {
|
||||||
|
let start = p.next_start();
|
||||||
|
p.assert(Token::Numbering(number));
|
||||||
|
let body = tree_indented(p);
|
||||||
|
Node::Enum(EnumItem { span: p.span(start), number, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
@ -500,7 +503,9 @@ fn block(p: &mut Parser, scoping: bool) -> Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.end_group();
|
p.end_group();
|
||||||
p.skip_white();
|
|
||||||
|
// Forcefully skip over newlines since the group's contents can't.
|
||||||
|
p.eat_while(|t| matches!(t, Token::Space(_)));
|
||||||
}
|
}
|
||||||
let span = p.end_group();
|
let span = p.end_group();
|
||||||
Expr::Block(BlockExpr { span, exprs, scoping })
|
Expr::Block(BlockExpr { span, exprs, scoping })
|
||||||
|
@ -242,6 +242,16 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consume tokens while the condition is true.
|
||||||
|
pub fn eat_while<F>(&mut self, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Token<'s>) -> bool,
|
||||||
|
{
|
||||||
|
while self.peek().map_or(false, |t| f(t)) {
|
||||||
|
self.eat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume the next token if the closure maps it a to `Some`-variant.
|
/// Consume the next token if the closure maps it a to `Some`-variant.
|
||||||
pub fn eat_map<T, F>(&mut self, f: F) -> Option<T>
|
pub fn eat_map<T, F>(&mut self, f: F) -> Option<T>
|
||||||
where
|
where
|
||||||
@ -278,18 +288,6 @@ impl<'s> Parser<'s> {
|
|||||||
debug_assert_eq!(next, Some(t));
|
debug_assert_eq!(next, Some(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skip whitespace and comment tokens.
|
|
||||||
pub fn skip_white(&mut self) {
|
|
||||||
while matches!(
|
|
||||||
self.peek(),
|
|
||||||
Some(Token::Space(_)) |
|
|
||||||
Some(Token::LineComment(_)) |
|
|
||||||
Some(Token::BlockComment(_))
|
|
||||||
) {
|
|
||||||
self.eat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The index at which the last token ended.
|
/// The index at which the last token ended.
|
||||||
///
|
///
|
||||||
/// Refers to the end of the last _non-whitespace_ token in code mode.
|
/// Refers to the end of the last _non-whitespace_ token in code mode.
|
||||||
|
@ -102,6 +102,7 @@ impl<'s> Tokens<'s> {
|
|||||||
'`' => self.raw(),
|
'`' => self.raw(),
|
||||||
'$' => self.math(),
|
'$' => self.math(),
|
||||||
'-' => self.hyph(start),
|
'-' => self.hyph(start),
|
||||||
|
c if c == '.' || c.is_ascii_digit() => self.numbering(start, c),
|
||||||
|
|
||||||
// Plain text.
|
// Plain text.
|
||||||
_ => self.text(start),
|
_ => self.text(start),
|
||||||
@ -185,11 +186,11 @@ impl<'s> Tokens<'s> {
|
|||||||
// Whitespace.
|
// Whitespace.
|
||||||
c if c.is_whitespace() => true,
|
c if c.is_whitespace() => true,
|
||||||
// Comments.
|
// Comments.
|
||||||
'/' if self.s.check(|c| c == '/' || c == '*') => true,
|
'/' => true,
|
||||||
// Parentheses.
|
// Parentheses.
|
||||||
'[' | ']' | '{' | '}' => true,
|
'[' | ']' | '{' | '}' => true,
|
||||||
// Markup.
|
// Markup.
|
||||||
'#' | '~' | '*' | '_' | '-' | '`' | '$' => true,
|
'#' | '~' | '*' | '_' | '`' | '$' | '-' => true,
|
||||||
// Escaping.
|
// Escaping.
|
||||||
'\\' => true,
|
'\\' => true,
|
||||||
// Just text.
|
// Just text.
|
||||||
@ -274,6 +275,25 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn numbering(&mut self, start: usize, c: char) -> Token<'s> {
|
||||||
|
let number = if c != '.' {
|
||||||
|
self.s.eat_while(|c| c.is_ascii_digit());
|
||||||
|
let read = self.s.eaten_from(start);
|
||||||
|
if !self.s.eat_if('.') {
|
||||||
|
return Token::Text(read);
|
||||||
|
}
|
||||||
|
read.parse().ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.s.check(|c| !c.is_whitespace()) {
|
||||||
|
return Token::Text(self.s.eaten_from(start));
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Numbering(number)
|
||||||
|
}
|
||||||
|
|
||||||
fn raw(&mut self) -> Token<'s> {
|
fn raw(&mut self) -> Token<'s> {
|
||||||
let mut backticks = 1;
|
let mut backticks = 1;
|
||||||
while self.s.eat_if('`') {
|
while self.s.eat_if('`') {
|
||||||
@ -357,12 +377,12 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(&mut self, start: usize, first: char) -> Token<'s> {
|
fn number(&mut self, start: usize, c: char) -> Token<'s> {
|
||||||
// Read the first part (integer or fractional depending on `first`).
|
// Read the first part (integer or fractional depending on `first`).
|
||||||
self.s.eat_while(|c| c.is_ascii_digit());
|
self.s.eat_while(|c| c.is_ascii_digit());
|
||||||
|
|
||||||
// Read the fractional part if not already done and present.
|
// Read the fractional part if not already done.
|
||||||
if first != '.' && self.s.eat_if('.') {
|
if c != '.' && self.s.eat_if('.') {
|
||||||
self.s.eat_while(|c| c.is_ascii_digit());
|
self.s.eat_while(|c| c.is_ascii_digit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +674,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test code symbols in text.
|
// Test code symbols in text.
|
||||||
t!(Markup[" /"]: "a():\"b" => Text("a():\"b"));
|
t!(Markup[" /"]: "a():\"b" => Text("a():\"b"));
|
||||||
t!(Markup[" /"]: ";:,|/+" => Text(";:,|/+"));
|
t!(Markup[" /"]: ";:,|/+" => Text(";:,|"), Text("/+"));
|
||||||
t!(Markup[" /"]: "#-a" => Text("#"), Text("-"), Text("a"));
|
t!(Markup[" /"]: "#-a" => Text("#"), Text("-"), Text("a"));
|
||||||
t!(Markup[" "]: "#123" => Text("#"), Text("123"));
|
t!(Markup[" "]: "#123" => Text("#"), Text("123"));
|
||||||
|
|
||||||
@ -707,10 +727,14 @@ mod tests {
|
|||||||
t!(Markup: "_" => Underscore);
|
t!(Markup: "_" => Underscore);
|
||||||
t!(Markup[""]: "###" => Hashtag, Hashtag, Hashtag);
|
t!(Markup[""]: "###" => Hashtag, Hashtag, Hashtag);
|
||||||
t!(Markup["a1/"]: "# " => Hashtag, Space(0));
|
t!(Markup["a1/"]: "# " => Hashtag, Space(0));
|
||||||
t!(Markup["a1/"]: "- " => Hyph, Space(0));
|
|
||||||
t!(Markup: "~" => Tilde);
|
t!(Markup: "~" => Tilde);
|
||||||
t!(Markup[" "]: r"\" => Backslash);
|
t!(Markup[" "]: r"\" => Backslash);
|
||||||
t!(Markup["a "]: r"a--" => Text("a"), HyphHyph);
|
t!(Markup["a "]: r"a--" => Text("a"), HyphHyph);
|
||||||
|
t!(Markup["a1/"]: "- " => Hyph, Space(0));
|
||||||
|
t!(Markup[" "]: "." => Numbering(None));
|
||||||
|
t!(Markup[" "]: "1." => Numbering(Some(1)));
|
||||||
|
t!(Markup[" "]: "1.a" => Text("1."), Text("a"));
|
||||||
|
t!(Markup[" /"]: "a1." => Text("a1."));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -97,13 +97,14 @@ impl Pretty for Node {
|
|||||||
Self::Strong(_) => p.push('*'),
|
Self::Strong(_) => p.push('*'),
|
||||||
Self::Emph(_) => p.push('_'),
|
Self::Emph(_) => p.push('_'),
|
||||||
Self::Raw(raw) => raw.pretty(p),
|
Self::Raw(raw) => raw.pretty(p),
|
||||||
Self::Heading(heading) => heading.pretty(p),
|
Self::Heading(n) => n.pretty(p),
|
||||||
Self::List(list) => list.pretty(p),
|
Self::List(n) => n.pretty(p),
|
||||||
Self::Expr(expr) => {
|
Self::Enum(n) => n.pretty(p),
|
||||||
if expr.has_short_form() {
|
Self::Expr(n) => {
|
||||||
|
if n.has_short_form() {
|
||||||
p.push('#');
|
p.push('#');
|
||||||
}
|
}
|
||||||
expr.pretty(p);
|
n.pretty(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,13 +176,23 @@ impl Pretty for HeadingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ListNode {
|
impl Pretty for ListItem {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
p.push_str("- ");
|
p.push_str("- ");
|
||||||
self.body.pretty(p);
|
self.body.pretty(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pretty for EnumItem {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
if let Some(number) = self.number {
|
||||||
|
write!(p, "{}", number).unwrap();
|
||||||
|
}
|
||||||
|
p.push_str(". ");
|
||||||
|
self.body.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pretty for Expr {
|
impl Pretty for Expr {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -21,8 +21,10 @@ pub enum Node {
|
|||||||
Raw(RawNode),
|
Raw(RawNode),
|
||||||
/// A section heading: `= Introduction`.
|
/// A section heading: `= Introduction`.
|
||||||
Heading(HeadingNode),
|
Heading(HeadingNode),
|
||||||
/// A single list item: `- ...`.
|
/// An item in an unordered list: `- ...`.
|
||||||
List(ListNode),
|
List(ListItem),
|
||||||
|
/// An item in an enumeration (ordered list): `1. ...`.
|
||||||
|
Enum(EnumItem),
|
||||||
/// An expression.
|
/// An expression.
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
}
|
}
|
||||||
@ -115,11 +117,22 @@ pub struct HeadingNode {
|
|||||||
pub body: Rc<Tree>,
|
pub body: Rc<Tree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single list item: `- ...`.
|
/// An item in an unordered list: `- ...`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ListNode {
|
pub struct ListItem {
|
||||||
/// The source code location.
|
/// The source code location.
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// The contents of the list item.
|
/// The contents of the list item.
|
||||||
pub body: Tree,
|
pub body: Tree,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An item in an enumeration (ordered list): `1. ...`.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct EnumItem {
|
||||||
|
/// The source code location.
|
||||||
|
pub span: Span,
|
||||||
|
/// The number, if any.
|
||||||
|
pub number: Option<usize>,
|
||||||
|
/// The contents of the list item.
|
||||||
|
pub body: Tree,
|
||||||
|
}
|
||||||
|
@ -118,6 +118,10 @@ pub enum Token<'s> {
|
|||||||
/// One or two dollar signs followed by inner contents, terminated with the
|
/// One or two dollar signs followed by inner contents, terminated with the
|
||||||
/// same number of dollar signs.
|
/// same number of dollar signs.
|
||||||
Math(MathToken<'s>),
|
Math(MathToken<'s>),
|
||||||
|
/// A numbering: `23.`.
|
||||||
|
///
|
||||||
|
/// Can also exist without the number: `.`.
|
||||||
|
Numbering(Option<usize>),
|
||||||
/// An identifier: `center`.
|
/// An identifier: `center`.
|
||||||
Ident(&'s str),
|
Ident(&'s str),
|
||||||
/// A boolean: `true`, `false`.
|
/// A boolean: `true`, `false`.
|
||||||
@ -256,6 +260,7 @@ impl<'s> Token<'s> {
|
|||||||
Self::UnicodeEscape(_) => "unicode escape sequence",
|
Self::UnicodeEscape(_) => "unicode escape sequence",
|
||||||
Self::Raw(_) => "raw block",
|
Self::Raw(_) => "raw block",
|
||||||
Self::Math(_) => "math formula",
|
Self::Math(_) => "math formula",
|
||||||
|
Self::Numbering(_) => "numbering",
|
||||||
Self::Ident(_) => "identifier",
|
Self::Ident(_) => "identifier",
|
||||||
Self::Bool(_) => "boolean",
|
Self::Bool(_) => "boolean",
|
||||||
Self::Int(_) => "integer",
|
Self::Int(_) => "integer",
|
||||||
|
@ -59,6 +59,7 @@ visit! {
|
|||||||
Node::Raw(_) => {}
|
Node::Raw(_) => {}
|
||||||
Node::Heading(n) => v.visit_heading(n),
|
Node::Heading(n) => v.visit_heading(n),
|
||||||
Node::List(n) => v.visit_list(n),
|
Node::List(n) => v.visit_list(n),
|
||||||
|
Node::Enum(n) => v.visit_enum(n),
|
||||||
Node::Expr(n) => v.visit_expr(n),
|
Node::Expr(n) => v.visit_expr(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +68,11 @@ visit! {
|
|||||||
v.visit_tree(&node.body);
|
v.visit_tree(&node.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_list(v, node: &ListNode) {
|
fn visit_list(v, node: &ListItem) {
|
||||||
|
v.visit_tree(&node.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_enum(v, node: &EnumItem) {
|
||||||
v.visit_tree(&node.body);
|
v.visit_tree(&node.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
tests/ref/markup/enums.png
Normal file
BIN
tests/ref/markup/enums.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
11
tests/typ/markup/enums.typ
Normal file
11
tests/typ/markup/enums.typ
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Test enums.
|
||||||
|
|
||||||
|
---
|
||||||
|
1. Embrace
|
||||||
|
2. Extend
|
||||||
|
3. Extinguish
|
||||||
|
|
||||||
|
---
|
||||||
|
1. First.
|
||||||
|
2. Second.
|
||||||
|
1. Back to first.
|
Loading…
x
Reference in New Issue
Block a user