mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Switch to = for headings once again
This commit is contained in:
parent
f85e5aac64
commit
5c327e249e
@ -102,14 +102,12 @@ 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 if *at_start => return Some(heading(p)),
|
Token::Eq if *at_start => return Some(heading(p)),
|
||||||
Token::Hyph if *at_start => return Some(list_item(p)),
|
Token::Hyph if *at_start => return Some(list_item(p)),
|
||||||
Token::Numbering(number) if *at_start => return Some(enum_item(p, number)),
|
Token::Numbering(number) if *at_start => return Some(enum_item(p, number)),
|
||||||
|
|
||||||
// Line-based markup that is not currently at the start of the line.
|
// Line-based markup that is not currently at the start of the line.
|
||||||
Token::Hashtag | Token::Hyph | Token::Numbering(_) => {
|
Token::Eq | Token::Hyph | Token::Numbering(_) => Node::Text(p.peek_src().into()),
|
||||||
Node::Text(p.peek_src().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hashtag + keyword / identifier.
|
// Hashtag + keyword / identifier.
|
||||||
Token::Ident(_)
|
Token::Ident(_)
|
||||||
@ -183,11 +181,11 @@ fn raw(p: &mut Parser, token: RawToken) -> Node {
|
|||||||
/// Parse a heading.
|
/// Parse a heading.
|
||||||
fn heading(p: &mut Parser) -> Node {
|
fn heading(p: &mut Parser) -> Node {
|
||||||
let start = p.next_start();
|
let start = p.next_start();
|
||||||
p.assert(Token::Hashtag);
|
p.assert(Token::Eq);
|
||||||
|
|
||||||
// Count depth.
|
// Count depth.
|
||||||
let mut level: usize = 1;
|
let mut level: usize = 1;
|
||||||
while p.eat_if(Token::Hashtag) {
|
while p.eat_if(Token::Eq) {
|
||||||
level += 1;
|
level += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,12 +88,12 @@ impl<'s> Scanner<'s> {
|
|||||||
|
|
||||||
/// Checks whether the next char fulfills a condition.
|
/// Checks whether the next char fulfills a condition.
|
||||||
///
|
///
|
||||||
/// Returns `false` if there is no next char.
|
/// Returns `default` if there is no next char.
|
||||||
pub fn check<F>(&self, f: F) -> bool
|
pub fn check_or<F>(&self, default: bool, f: F) -> bool
|
||||||
where
|
where
|
||||||
F: FnOnce(char) -> bool,
|
F: FnOnce(char) -> bool,
|
||||||
{
|
{
|
||||||
self.peek().map(f).unwrap_or(false)
|
self.peek().map_or(default, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The previous index in the source string.
|
/// The previous index in the source string.
|
||||||
|
@ -89,19 +89,22 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
impl<'s> Tokens<'s> {
|
impl<'s> Tokens<'s> {
|
||||||
fn markup(&mut self, start: usize, c: char) -> Token<'s> {
|
fn markup(&mut self, start: usize, c: char) -> Token<'s> {
|
||||||
match c {
|
match c {
|
||||||
|
// Escape sequences.
|
||||||
|
'\\' => self.backslash(),
|
||||||
|
|
||||||
|
// Keywords and identifiers.
|
||||||
|
'#' => self.hash(),
|
||||||
|
|
||||||
// Markup.
|
// Markup.
|
||||||
'~' => Token::Tilde,
|
'~' => Token::Tilde,
|
||||||
'*' => Token::Star,
|
'*' => Token::Star,
|
||||||
'_' => Token::Underscore,
|
'_' => Token::Underscore,
|
||||||
'\\' => self.backslash(),
|
|
||||||
'`' => self.raw(),
|
'`' => self.raw(),
|
||||||
'$' => self.math(),
|
'$' => self.math(),
|
||||||
'-' => self.hyph(start),
|
'-' => self.hyph(start),
|
||||||
|
'=' if self.s.check_or(true, |c| c == '=' || c.is_whitespace()) => Token::Eq,
|
||||||
c if c == '.' || c.is_ascii_digit() => self.numbering(start, c),
|
c if c == '.' || c.is_ascii_digit() => self.numbering(start, c),
|
||||||
|
|
||||||
// Headings, keywords and identifiers.
|
|
||||||
'#' => self.hash(start),
|
|
||||||
|
|
||||||
// Plain text.
|
// Plain text.
|
||||||
_ => self.text(start),
|
_ => self.text(start),
|
||||||
}
|
}
|
||||||
@ -143,7 +146,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
// Numbers.
|
// Numbers.
|
||||||
c if c.is_ascii_digit()
|
c if c.is_ascii_digit()
|
||||||
|| (c == '.' && self.s.check(|n| n.is_ascii_digit())) =>
|
|| (c == '.' && self.s.check_or(false, |n| n.is_ascii_digit())) =>
|
||||||
{
|
{
|
||||||
self.number(start, c)
|
self.number(start, c)
|
||||||
}
|
}
|
||||||
@ -157,7 +160,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
fn whitespace(&mut self, first: char) -> Token<'s> {
|
fn whitespace(&mut self, first: char) -> Token<'s> {
|
||||||
// Fast path for just a single space
|
// Fast path for just a single space
|
||||||
if first == ' ' && !self.s.check(char::is_whitespace) {
|
if first == ' ' && self.s.check_or(true, |c| !c.is_whitespace()) {
|
||||||
Token::Space(0)
|
Token::Space(0)
|
||||||
} else {
|
} else {
|
||||||
self.s.uneat();
|
self.s.uneat();
|
||||||
@ -188,8 +191,10 @@ impl<'s> Tokens<'s> {
|
|||||||
'/' => true,
|
'/' => true,
|
||||||
// Parentheses.
|
// Parentheses.
|
||||||
'[' | ']' | '{' | '}' => true,
|
'[' | ']' | '{' | '}' => true,
|
||||||
|
// Code.
|
||||||
|
'#' => true,
|
||||||
// Markup.
|
// Markup.
|
||||||
'#' | '~' | '*' | '_' | '`' | '$' | '-' => true,
|
'~' | '*' | '_' | '`' | '$' | '-' => true,
|
||||||
// Escaping.
|
// Escaping.
|
||||||
'\\' => true,
|
'\\' => true,
|
||||||
// Just text.
|
// Just text.
|
||||||
@ -233,18 +238,16 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash(&mut self, start: usize) -> Token<'s> {
|
fn hash(&mut self) -> Token<'s> {
|
||||||
if self.s.check(is_id_start) {
|
if self.s.check_or(false, is_id_start) {
|
||||||
let read = self.s.eat_while(is_id_continue);
|
let read = self.s.eat_while(is_id_continue);
|
||||||
if let Some(keyword) = keyword(read) {
|
if let Some(keyword) = keyword(read) {
|
||||||
keyword
|
keyword
|
||||||
} else {
|
} else {
|
||||||
Token::Ident(read)
|
Token::Ident(read)
|
||||||
}
|
}
|
||||||
} else if self.s.check(|c| c != '#' && !c.is_whitespace()) {
|
|
||||||
Token::Text(self.s.eaten_from(start))
|
|
||||||
} else {
|
} else {
|
||||||
Token::Hashtag
|
Token::Invalid("#")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,10 +258,10 @@ impl<'s> Tokens<'s> {
|
|||||||
} else {
|
} else {
|
||||||
Token::HyphHyph
|
Token::HyphHyph
|
||||||
}
|
}
|
||||||
} else if self.s.check(|c| !c.is_whitespace()) {
|
} else if self.s.check_or(true, char::is_whitespace) {
|
||||||
Token::Text(self.s.eaten_from(start))
|
|
||||||
} else {
|
|
||||||
Token::Hyph
|
Token::Hyph
|
||||||
|
} else {
|
||||||
|
Token::Text(self.s.eaten_from(start))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,11 +277,11 @@ impl<'s> Tokens<'s> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.s.check(|c| !c.is_whitespace()) {
|
if self.s.check_or(true, char::is_whitespace) {
|
||||||
return Token::Text(self.s.eaten_from(start));
|
Token::Numbering(number)
|
||||||
|
} else {
|
||||||
|
Token::Text(self.s.eaten_from(start))
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Numbering(number)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw(&mut self) -> Token<'s> {
|
fn raw(&mut self) -> Token<'s> {
|
||||||
@ -663,8 +666,8 @@ 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(";:,|"), 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" => Invalid("#"), Text("123"));
|
||||||
|
|
||||||
// Test text ends.
|
// Test text ends.
|
||||||
t!(Markup[""]: "hello " => Text("hello"), Space(0));
|
t!(Markup[""]: "hello " => Text("hello"), Space(0));
|
||||||
@ -712,8 +715,8 @@ mod tests {
|
|||||||
// Test markup tokens.
|
// Test markup tokens.
|
||||||
t!(Markup[" a1"]: "*" => Star);
|
t!(Markup[" a1"]: "*" => Star);
|
||||||
t!(Markup: "_" => Underscore);
|
t!(Markup: "_" => Underscore);
|
||||||
t!(Markup[""]: "###" => Hashtag, Hashtag, Hashtag);
|
t!(Markup[""]: "===" => Eq, Eq, Eq);
|
||||||
t!(Markup["a1/"]: "# " => Hashtag, Space(0));
|
t!(Markup["a1/"]: "= " => Eq, 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);
|
||||||
@ -776,7 +779,7 @@ mod tests {
|
|||||||
for &(s, t) in &list {
|
for &(s, t) in &list {
|
||||||
t!(Markup[" "]: format!("#{}", s) => t);
|
t!(Markup[" "]: format!("#{}", s) => t);
|
||||||
t!(Markup[" "]: format!("#{0}#{0}", s) => t, t);
|
t!(Markup[" "]: format!("#{0}#{0}", s) => t, t);
|
||||||
t!(Markup[" /"]: format!("# {}", s) => Token::Hashtag, Space(0), Text(s));
|
t!(Markup[" /"]: format!("# {}", s) => Token::Invalid("#"), Space(0), Text(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
for &(s, t) in &list {
|
for &(s, t) in &list {
|
||||||
|
@ -169,7 +169,7 @@ impl Pretty for RawNode {
|
|||||||
impl Pretty for HeadingNode {
|
impl Pretty for HeadingNode {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
for _ in 0 .. self.level {
|
for _ in 0 .. self.level {
|
||||||
p.push('#');
|
p.push('=');
|
||||||
}
|
}
|
||||||
p.push(' ');
|
p.push(' ');
|
||||||
self.body.pretty(p);
|
self.body.pretty(p);
|
||||||
@ -653,7 +653,7 @@ mod tests {
|
|||||||
roundtrip("\\ ");
|
roundtrip("\\ ");
|
||||||
roundtrip("\n\n");
|
roundtrip("\n\n");
|
||||||
roundtrip("hi");
|
roundtrip("hi");
|
||||||
roundtrip("# *Ok*");
|
roundtrip("= *Ok*");
|
||||||
roundtrip("- Ok");
|
roundtrip("- Ok");
|
||||||
|
|
||||||
// Raw.
|
// Raw.
|
||||||
|
@ -19,8 +19,6 @@ pub enum Token<'s> {
|
|||||||
Star,
|
Star,
|
||||||
/// An underscore: `_`.
|
/// An underscore: `_`.
|
||||||
Underscore,
|
Underscore,
|
||||||
/// A single hashtag: `#`.
|
|
||||||
Hashtag,
|
|
||||||
/// A tilde: `~`.
|
/// A tilde: `~`.
|
||||||
Tilde,
|
Tilde,
|
||||||
/// Two hyphens: `--`.
|
/// Two hyphens: `--`.
|
||||||
@ -215,7 +213,6 @@ impl<'s> Token<'s> {
|
|||||||
Self::RightParen => "closing paren",
|
Self::RightParen => "closing paren",
|
||||||
Self::Star => "star",
|
Self::Star => "star",
|
||||||
Self::Underscore => "underscore",
|
Self::Underscore => "underscore",
|
||||||
Self::Hashtag => "hashtag",
|
|
||||||
Self::Tilde => "tilde",
|
Self::Tilde => "tilde",
|
||||||
Self::HyphHyph => "en dash",
|
Self::HyphHyph => "en dash",
|
||||||
Self::HyphHyphHyph => "em dash",
|
Self::HyphHyphHyph => "em dash",
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.2 KiB |
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test in heading.
|
// Test in heading.
|
||||||
# A #align!(right) B
|
= A #align!(right) B
|
||||||
C
|
C
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#let name = "Klaus"
|
#let name = "Klaus"
|
||||||
|
|
||||||
## Chapter 1
|
== Chapter 1
|
||||||
#name stood in a field of wheat. There was nothing of particular interest about
|
#name stood in a field of wheat. There was nothing of particular interest about
|
||||||
the field #name just casually surveyed for any paths on which the corn would not
|
the field #name just casually surveyed for any paths on which the corn would not
|
||||||
totally ruin his semi-new outdorsy jacket but then again, most of us spend
|
totally ruin his semi-new outdorsy jacket but then again, most of us spend
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#let name = "Klaus"
|
#let name = "Klaus"
|
||||||
|
|
||||||
## Chapter 2
|
== Chapter 2
|
||||||
Their motivations, however, were pretty descript, so to speak. #name had not yet
|
Their motivations, however, were pretty descript, so to speak. #name had not yet
|
||||||
conceptualized their consequences, but that should change pretty quickly. #name
|
conceptualized their consequences, but that should change pretty quickly. #name
|
||||||
approached the center of the field and picked up a 4-foot long disk made from
|
approached the center of the field and picked up a 4-foot long disk made from
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Test include statements.
|
// Test include statements.
|
||||||
|
|
||||||
---
|
---
|
||||||
# Document
|
= Document
|
||||||
|
|
||||||
// Include a file
|
// Include a file
|
||||||
#include "importable/chap1.typ"
|
#include "importable/chap1.typ"
|
||||||
|
@ -79,6 +79,6 @@ Three
|
|||||||
// Error: 9 expected expression
|
// Error: 9 expected expression
|
||||||
#let v =
|
#let v =
|
||||||
|
|
||||||
// Should output `= 1`.
|
// Should output a heading `1`.
|
||||||
// Error: 6-9 expected identifier, found string
|
// Error: 6-9 expected identifier, found string
|
||||||
#let "v" = 1
|
#let "v" = 1
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
// the parentheses.
|
// the parentheses.
|
||||||
#align(center)[
|
#align(center)[
|
||||||
// Markdown-like syntax for headings.
|
// Markdown-like syntax for headings.
|
||||||
#### 3. Übungsblatt Computerorientierte Mathematik II #v(4mm)
|
==== 3. Übungsblatt Computerorientierte Mathematik II #v(4mm)
|
||||||
*Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(4mm)
|
*Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(4mm)
|
||||||
*Alle Antworten sind zu beweisen.*
|
*Alle Antworten sind zu beweisen.*
|
||||||
]
|
]
|
||||||
|
@ -4,39 +4,39 @@
|
|||||||
// Different number of hashtags.
|
// Different number of hashtags.
|
||||||
|
|
||||||
// Valid levels.
|
// Valid levels.
|
||||||
# Level 1
|
= Level 1
|
||||||
### Level 2
|
=== Level 2
|
||||||
###### Level 6
|
====== Level 6
|
||||||
|
|
||||||
// Too many hashtags.
|
// Too many hashtags.
|
||||||
// Warning: 1-8 should not exceed depth 6
|
// Warning: 1-8 should not exceed depth 6
|
||||||
####### Level 7
|
======= Level 7
|
||||||
|
|
||||||
---
|
---
|
||||||
// Heading vs. no heading.
|
// Heading vs. no heading.
|
||||||
|
|
||||||
// Parsed as headings if at start of the context.
|
// Parsed as headings if at start of the context.
|
||||||
/**/ # Level 1
|
/**/ = Level 1
|
||||||
{[## Level 2]}
|
{[== Level 2]}
|
||||||
#box[### Level 3]
|
#box[=== Level 3]
|
||||||
|
|
||||||
// Not at the start of the context.
|
// Not at the start of the context.
|
||||||
No # heading
|
No = heading
|
||||||
|
|
||||||
// Escaped.
|
// Escaped.
|
||||||
\# No heading
|
\= No heading
|
||||||
|
|
||||||
---
|
---
|
||||||
// While indented at least as much as the start, the heading continues.
|
// While indented at least as much as the start, the heading continues.
|
||||||
|
|
||||||
# This
|
= This
|
||||||
is
|
is
|
||||||
indented.
|
indented.
|
||||||
|
|
||||||
# This
|
= This
|
||||||
is not.
|
is not.
|
||||||
|
|
||||||
// Code blocks continue heading.
|
// Code blocks continue heading.
|
||||||
# A {
|
= A {
|
||||||
"B"
|
"B"
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
{
|
{
|
||||||
"name": "markup.heading.typst",
|
"name": "markup.heading.typst",
|
||||||
"contentName": "entity.name.section.typst",
|
"contentName": "entity.name.section.typst",
|
||||||
"begin": "^\\s*#{1,6}\\s+",
|
"begin": "^\\s*={1,6}\\s+",
|
||||||
"end": "\n",
|
"end": "\n",
|
||||||
"beginCaptures": { "0": { "name": "punctuation.definition.heading.typst" } },
|
"beginCaptures": { "0": { "name": "punctuation.definition.heading.typst" } },
|
||||||
"patterns": [{ "include": "#markup" }]
|
"patterns": [{ "include": "#markup" }]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user