From 50dcacea9a3d9284ef1eeb9c20682d9568c91e70 Mon Sep 17 00:00:00 2001 From: Ian Wrzesinski <133046678+wrzian@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:23:14 -0500 Subject: [PATCH] Convert unopened square-brackets into a hard error (#5414) --- crates/typst-syntax/src/parser.rs | 45 +++++++++++++++-------------- tests/ref/single-right-bracket.png | Bin 118 -> 0 bytes tests/suite/scripting/blocks.typ | 37 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 21 deletions(-) delete mode 100644 tests/ref/single-right-bracket.png diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs index e087f9dd3..cb5e2dd85 100644 --- a/crates/typst-syntax/src/parser.rs +++ b/crates/typst-syntax/src/parser.rs @@ -47,14 +47,9 @@ fn markup_exprs(p: &mut Parser, mut at_start: bool, stop_set: SyntaxSet) { debug_assert!(stop_set.contains(SyntaxKind::End)); at_start |= p.had_newline(); let mut nesting: usize = 0; - loop { - match p.current() { - SyntaxKind::LeftBracket => nesting += 1, - SyntaxKind::RightBracket if nesting > 0 => nesting -= 1, - _ if p.at_set(stop_set) => break, - _ => {} - } - markup_expr(p, at_start); + // Keep going if we're at a nested right-bracket regardless of the stop set. + while !p.at_set(stop_set) || (nesting > 0 && p.at(SyntaxKind::RightBracket)) { + markup_expr(p, at_start, &mut nesting); at_start = p.had_newline(); } } @@ -69,15 +64,12 @@ pub(super) fn reparse_markup( ) -> Option> { let mut p = Parser::new(text, range.start, LexMode::Markup); *at_start |= p.had_newline(); - while p.current_start() < range.end { - match p.current() { - SyntaxKind::LeftBracket => *nesting += 1, - SyntaxKind::RightBracket if *nesting > 0 => *nesting -= 1, - SyntaxKind::RightBracket if !top_level => break, - SyntaxKind::End => break, - _ => {} + while !p.end() && p.current_start() < range.end { + // If not top-level and at a new RightBracket, stop the reparse. + if !top_level && *nesting == 0 && p.at(SyntaxKind::RightBracket) { + break; } - markup_expr(&mut p, *at_start); + markup_expr(&mut p, *at_start, nesting); *at_start = p.had_newline(); } (p.balanced && p.current_start() == range.end).then(|| p.finish()) @@ -86,8 +78,21 @@ pub(super) fn reparse_markup( /// Parses a single markup expression. This includes markup elements like text, /// headings, strong/emph, lists/enums, etc. This is also the entry point for /// parsing math equations and embedded code expressions. -fn markup_expr(p: &mut Parser, at_start: bool) { +fn markup_expr(p: &mut Parser, at_start: bool, nesting: &mut usize) { match p.current() { + SyntaxKind::LeftBracket => { + *nesting += 1; + p.convert_and_eat(SyntaxKind::Text); + } + SyntaxKind::RightBracket if *nesting > 0 => { + *nesting -= 1; + p.convert_and_eat(SyntaxKind::Text); + } + SyntaxKind::RightBracket => { + p.unexpected(); + p.hint("try using a backslash escape: \\]"); + } + SyntaxKind::Text | SyntaxKind::Linebreak | SyntaxKind::Escape @@ -108,9 +113,7 @@ fn markup_expr(p: &mut Parser, at_start: bool) { SyntaxKind::RefMarker => reference(p), SyntaxKind::Dollar => equation(p), - SyntaxKind::LeftBracket - | SyntaxKind::RightBracket - | SyntaxKind::HeadingMarker + SyntaxKind::HeadingMarker | SyntaxKind::ListMarker | SyntaxKind::EnumMarker | SyntaxKind::TermMarker @@ -201,7 +204,7 @@ fn equation(p: &mut Parser) { let m = p.marker(); p.enter_modes(LexMode::Math, AtNewline::Continue, |p| { p.assert(SyntaxKind::Dollar); - math(p, syntax_set!(Dollar, RightBracket, End)); + math(p, syntax_set!(Dollar, End)); p.expect_closing_delimiter(m, SyntaxKind::Dollar); }); p.wrap(m, SyntaxKind::Equation); diff --git a/tests/ref/single-right-bracket.png b/tests/ref/single-right-bracket.png deleted file mode 100644 index 9867424ddfa324301c82cc4dde8072d9dfaa899f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^6+kS_0VEhE<%|3RQnsEhjv*Ddl7HAcG$dYm6xi*q zE9WORe@;PCL(QJexeYq|46;71IC}Wpqgv*ak0;k1UYz=M#nHuL{ZT$l3=9kQOnGR} Rb7?8aKu=dcmvv4FO#p!jD