mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
No hashtag before else anymore
This commit is contained in:
parent
989d344d3d
commit
9e95502622
@ -654,12 +654,7 @@ fn if_expr(p: &mut Parser) -> Option<Expr> {
|
|||||||
if let Some(condition) = expr(p) {
|
if let Some(condition) = expr(p) {
|
||||||
if let Some(if_body) = body(p) {
|
if let Some(if_body) = body(p) {
|
||||||
let mut else_body = None;
|
let mut else_body = None;
|
||||||
|
if p.eat_if(Token::Else) {
|
||||||
// We are in code mode but still want to react to `#else` if the
|
|
||||||
// outer mode is markup.
|
|
||||||
if (p.outer_mode() == TokenMode::Code || p.eat_if(Token::Invalid("#")))
|
|
||||||
&& p.eat_if(Token::Else)
|
|
||||||
{
|
|
||||||
else_body = body(p);
|
else_body = body(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,15 +28,16 @@ pub struct Parser<'s> {
|
|||||||
|
|
||||||
/// A logical group of tokens, e.g. `[...]`.
|
/// A logical group of tokens, e.g. `[...]`.
|
||||||
struct GroupEntry {
|
struct GroupEntry {
|
||||||
/// The start index of the group. Used by `Parser::end_group` to return the
|
|
||||||
/// group's full span.
|
|
||||||
pub start: usize,
|
|
||||||
/// The kind of group this is. This decides which tokens will end the group.
|
/// The kind of group this is. This decides which tokens will end the group.
|
||||||
/// For example, a [`Group::Paren`] will be ended by
|
/// For example, a [`Group::Paren`] will be ended by
|
||||||
/// [`Token::RightParen`].
|
/// [`Token::RightParen`].
|
||||||
pub kind: Group,
|
pub kind: Group,
|
||||||
/// The mode the parser was in _before_ the group started.
|
/// The start index of the group. Used by `Parser::end_group` to return the
|
||||||
pub outer_mode: TokenMode,
|
/// group's full span.
|
||||||
|
pub start: usize,
|
||||||
|
/// The mode the parser was in _before_ the group started (to which we go
|
||||||
|
/// back once the group ends).
|
||||||
|
pub prev_mode: TokenMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A group, confined by optional start and end delimiters.
|
/// A group, confined by optional start and end delimiters.
|
||||||
@ -232,9 +233,9 @@ impl<'s> Parser<'s> {
|
|||||||
/// This panics if the next token does not start the given group.
|
/// This panics if the next token does not start the given group.
|
||||||
pub fn start_group(&mut self, kind: Group, mode: TokenMode) {
|
pub fn start_group(&mut self, kind: Group, mode: TokenMode) {
|
||||||
self.groups.push(GroupEntry {
|
self.groups.push(GroupEntry {
|
||||||
start: self.next_start(),
|
|
||||||
kind,
|
kind,
|
||||||
outer_mode: self.tokens.mode(),
|
start: self.next_start(),
|
||||||
|
prev_mode: self.tokens.mode(),
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tokens.set_mode(mode);
|
self.tokens.set_mode(mode);
|
||||||
@ -256,7 +257,7 @@ impl<'s> Parser<'s> {
|
|||||||
pub fn end_group(&mut self) -> Span {
|
pub fn end_group(&mut self) -> Span {
|
||||||
let prev_mode = self.tokens.mode();
|
let prev_mode = self.tokens.mode();
|
||||||
let group = self.groups.pop().expect("no started group");
|
let group = self.groups.pop().expect("no started group");
|
||||||
self.tokens.set_mode(group.outer_mode);
|
self.tokens.set_mode(group.prev_mode);
|
||||||
self.repeek();
|
self.repeek();
|
||||||
|
|
||||||
let mut rescan = self.tokens.mode() != prev_mode;
|
let mut rescan = self.tokens.mode() != prev_mode;
|
||||||
@ -291,17 +292,6 @@ impl<'s> Parser<'s> {
|
|||||||
Span::new(self.id(), group.start, self.prev_end())
|
Span::new(self.id(), group.start, self.prev_end())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tokenization mode outside of the current group.
|
|
||||||
///
|
|
||||||
/// For example, this would be [`Markup`] if we are in a [`Code`] group that
|
|
||||||
/// is embedded in a [`Markup`] group.
|
|
||||||
///
|
|
||||||
/// [`Markup`]: TokenMode::Markup
|
|
||||||
/// [`Code`]: TokenMode::Code
|
|
||||||
pub fn outer_mode(&mut self) -> TokenMode {
|
|
||||||
self.groups.last().map_or(TokenMode::Markup, |group| group.outer_mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add an error with location and message.
|
/// Add an error with location and message.
|
||||||
pub fn error(&mut self, span: impl IntoSpan, message: impl Into<String>) {
|
pub fn error(&mut self, span: impl IntoSpan, message: impl Into<String>) {
|
||||||
self.errors.push(Error::new(span.into_span(self.id()), message));
|
self.errors.push(Error::new(span.into_span(self.id()), message));
|
||||||
@ -380,8 +370,10 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Whether the active group ends at a newline.
|
/// Whether the active group ends at a newline.
|
||||||
fn stop_at_newline(&self) -> bool {
|
fn stop_at_newline(&self) -> bool {
|
||||||
let active = self.groups.last().map(|group| group.kind);
|
matches!(
|
||||||
matches!(active, Some(Group::Stmt | Group::Expr | Group::Imports))
|
self.groups.last().map(|group| group.kind),
|
||||||
|
Some(Group::Stmt | Group::Expr | Group::Imports)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether we are inside the given group.
|
/// Whether we are inside the given group.
|
||||||
|
@ -419,7 +419,6 @@ impl Pretty for IfExpr {
|
|||||||
p.push(' ');
|
p.push(' ');
|
||||||
self.if_body.pretty(p);
|
self.if_body.pretty(p);
|
||||||
if let Some(expr) = &self.else_body {
|
if let Some(expr) = &self.else_body {
|
||||||
// FIXME: Hashtag in markup.
|
|
||||||
p.push_str(" else ");
|
p.push_str(" else ");
|
||||||
expr.pretty(p);
|
expr.pretty(p);
|
||||||
}
|
}
|
||||||
@ -603,7 +602,7 @@ mod tests {
|
|||||||
// Control flow.
|
// Control flow.
|
||||||
roundtrip("#let x = 1 + 2");
|
roundtrip("#let x = 1 + 2");
|
||||||
test_parse("#let f(x) = y", "#let f = (x) => y");
|
test_parse("#let f(x) = y", "#let f = (x) => y");
|
||||||
test_parse("#if x [y] #else [z]", "#if x [y] else [z]");
|
roundtrip("#if x [y] else [z]");
|
||||||
roundtrip("#while x {y}");
|
roundtrip("#while x {y}");
|
||||||
roundtrip("#for x in y {z}");
|
roundtrip("#for x in y {z}");
|
||||||
roundtrip("#for k, x in y {z}");
|
roundtrip("#for k, x in y {z}");
|
||||||
|
@ -27,14 +27,14 @@
|
|||||||
== 1
|
== 1
|
||||||
) [
|
) [
|
||||||
Nope.
|
Nope.
|
||||||
] #else {
|
] else {
|
||||||
"Three."
|
"Three."
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiline.
|
// Multiline.
|
||||||
#if false [
|
#if false [
|
||||||
Bad.
|
Bad.
|
||||||
] #else {
|
] else {
|
||||||
let point = "."
|
let point = "."
|
||||||
"Four" + point
|
"Four" + point
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@
|
|||||||
// Condition must be boolean.
|
// Condition must be boolean.
|
||||||
// If it isn't, neither branch is evaluated.
|
// If it isn't, neither branch is evaluated.
|
||||||
// Error: 5-14 expected boolean, found string
|
// Error: 5-14 expected boolean, found string
|
||||||
#if "a" + "b" { nope } #else { nope }
|
#if "a" + "b" { nope } else { nope }
|
||||||
|
|
||||||
---
|
---
|
||||||
// Make sure that we don't complain twice.
|
// Make sure that we don't complain twice.
|
||||||
@ -101,5 +101,9 @@ x {}
|
|||||||
#if x something
|
#if x something
|
||||||
|
|
||||||
// Should output `A thing.`
|
// Should output `A thing.`
|
||||||
// Error: 20 expected body
|
// Error: 19 expected body
|
||||||
A#if false {} #else thing
|
A#if false {} else thing
|
||||||
|
|
||||||
|
#if a []else [b]
|
||||||
|
#if a [] else [b]
|
||||||
|
#if a {} else [b]
|
||||||
|
@ -12,8 +12,8 @@ A#if true [B]C \
|
|||||||
A#if true [B] C \
|
A#if true [B] C \
|
||||||
A #if true{"B"}C \
|
A #if true{"B"}C \
|
||||||
A #if true{"B"} C \
|
A #if true{"B"} C \
|
||||||
A#if false [] #else [B]C \
|
A#if false [] else [B]C \
|
||||||
A#if true [B] #else [] C
|
A#if true [B] else [] C
|
||||||
|
|
||||||
---
|
---
|
||||||
// Spacing around while loop.
|
// Spacing around while loop.
|
||||||
|
@ -133,11 +133,11 @@
|
|||||||
"patterns": [{ "include": "#code" }]
|
"patterns": [{ "include": "#code" }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"begin": "(#)(if|else)\\b",
|
"begin": "((#)if|(?<=(}|])\\s*)else)\\b",
|
||||||
"end": "\n|(?=])|(?<=}|])",
|
"end": "\n|(?=])|(?<=}|])",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": { "name": "keyword.control.conditional.typst" },
|
"0": { "name": "keyword.control.conditional.typst" },
|
||||||
"1": { "name": "punctuation.definition.keyword.typst" }
|
"2": { "name": "punctuation.definition.keyword.typst" }
|
||||||
},
|
},
|
||||||
"patterns": [{ "include": "#code" }]
|
"patterns": [{ "include": "#code" }]
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user