From 9e9550262232158328ed7ccb2b620674e5015efc Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 30 Sep 2021 18:28:25 +0200 Subject: [PATCH] No hashtag before else anymore --- src/parse/mod.rs | 7 +----- src/parse/parser.rs | 34 +++++++++++------------------ src/syntax/pretty.rs | 3 +-- tests/typ/code/if.typ | 14 +++++++----- tests/typ/text/whitespace.typ | 4 ++-- tools/support/typst.tmLanguage.json | 4 ++-- 6 files changed, 28 insertions(+), 38 deletions(-) diff --git a/src/parse/mod.rs b/src/parse/mod.rs index ee18e6976..9cd176048 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -654,12 +654,7 @@ fn if_expr(p: &mut Parser) -> Option { if let Some(condition) = expr(p) { if let Some(if_body) = body(p) { let mut else_body = None; - - // 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) - { + if p.eat_if(Token::Else) { else_body = body(p); } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index c035319de..347d6f715 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -28,15 +28,16 @@ pub struct Parser<'s> { /// A logical group of tokens, e.g. `[...]`. 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. /// For example, a [`Group::Paren`] will be ended by /// [`Token::RightParen`]. pub kind: Group, - /// The mode the parser was in _before_ the group started. - pub outer_mode: TokenMode, + /// The start index of the group. Used by `Parser::end_group` to return the + /// 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. @@ -232,9 +233,9 @@ impl<'s> Parser<'s> { /// This panics if the next token does not start the given group. pub fn start_group(&mut self, kind: Group, mode: TokenMode) { self.groups.push(GroupEntry { - start: self.next_start(), kind, - outer_mode: self.tokens.mode(), + start: self.next_start(), + prev_mode: self.tokens.mode(), }); self.tokens.set_mode(mode); @@ -256,7 +257,7 @@ impl<'s> Parser<'s> { pub fn end_group(&mut self) -> Span { let prev_mode = self.tokens.mode(); 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(); 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()) } - /// 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. pub fn error(&mut self, span: impl IntoSpan, message: impl Into) { 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. fn stop_at_newline(&self) -> bool { - let active = self.groups.last().map(|group| group.kind); - matches!(active, Some(Group::Stmt | Group::Expr | Group::Imports)) + matches!( + self.groups.last().map(|group| group.kind), + Some(Group::Stmt | Group::Expr | Group::Imports) + ) } /// Whether we are inside the given group. diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 5e4d3ad24..d186eaf68 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -419,7 +419,6 @@ impl Pretty for IfExpr { p.push(' '); self.if_body.pretty(p); if let Some(expr) = &self.else_body { - // FIXME: Hashtag in markup. p.push_str(" else "); expr.pretty(p); } @@ -603,7 +602,7 @@ mod tests { // Control flow. roundtrip("#let x = 1 + 2"); 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("#for x in y {z}"); roundtrip("#for k, x in y {z}"); diff --git a/tests/typ/code/if.typ b/tests/typ/code/if.typ index 174e69785..36fd3d0f5 100644 --- a/tests/typ/code/if.typ +++ b/tests/typ/code/if.typ @@ -27,14 +27,14 @@ == 1 ) [ Nope. -] #else { +] else { "Three." } // Multiline. #if false [ Bad. -] #else { +] else { let point = "." "Four" + point } @@ -71,7 +71,7 @@ // Condition must be boolean. // If it isn't, neither branch is evaluated. // 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. @@ -101,5 +101,9 @@ x {} #if x something // Should output `A thing.` -// Error: 20 expected body -A#if false {} #else thing +// Error: 19 expected body +A#if false {} else thing + +#if a []else [b] +#if a [] else [b] +#if a {} else [b] diff --git a/tests/typ/text/whitespace.typ b/tests/typ/text/whitespace.typ index 7d5b1a689..aefdab340 100644 --- a/tests/typ/text/whitespace.typ +++ b/tests/typ/text/whitespace.typ @@ -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 false [] #else [B]C \ -A#if true [B] #else [] C +A#if false [] else [B]C \ +A#if true [B] else [] C --- // Spacing around while loop. diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json index 1c357da6e..1677da836 100644 --- a/tools/support/typst.tmLanguage.json +++ b/tools/support/typst.tmLanguage.json @@ -133,11 +133,11 @@ "patterns": [{ "include": "#code" }] }, { - "begin": "(#)(if|else)\\b", + "begin": "((#)if|(?<=(}|])\\s*)else)\\b", "end": "\n|(?=])|(?<=}|])", "beginCaptures": { "0": { "name": "keyword.control.conditional.typst" }, - "1": { "name": "punctuation.definition.keyword.typst" } + "2": { "name": "punctuation.definition.keyword.typst" } }, "patterns": [{ "include": "#code" }] },