diff options
| author | Ian Wrzesinski <133046678+wrzian@users.noreply.github.com> | 2024-12-08 11:23:14 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-08 16:23:14 +0000 |
| commit | 50dcacea9a3d9284ef1eeb9c20682d9568c91e70 (patch) | |
| tree | d3dce7228027aa67a457e437500db50d78e96180 | |
| parent | 0228462ba10397468b3c0937e2e9cd1d3118e0fc (diff) | |
Convert unopened square-brackets into a hard error (#5414)
| -rw-r--r-- | crates/typst-syntax/src/parser.rs | 45 | ||||
| -rw-r--r-- | tests/ref/single-right-bracket.png | bin | 118 -> 0 bytes | |||
| -rw-r--r-- | tests/suite/scripting/blocks.typ | 37 |
3 files changed, 61 insertions, 21 deletions
diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs index e087f9dd..cb5e2dd8 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<Vec<SyntaxNode>> { 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 Binary files differdeleted file mode 100644 index 9867424d..00000000 --- a/tests/ref/single-right-bracket.png +++ /dev/null diff --git a/tests/suite/scripting/blocks.typ b/tests/suite/scripting/blocks.typ index ba1d9c89..39cd37b2 100644 --- a/tests/suite/scripting/blocks.typ +++ b/tests/suite/scripting/blocks.typ @@ -136,8 +136,45 @@ #} --- single-right-bracket --- +// Error: 1-2 unexpected closing bracket +// Hint: 1-2 try using a backslash escape: \] ] +--- right-bracket-nesting --- +[ += [ Hi ]] +- how [ + - are ] +// Error: 10-11 unexpected closing bracket +// Hint: 10-11 try using a backslash escape: \] + - error][] +[[]] + +--- right-bracket-hash --- +// Error: 2-3 unexpected closing bracket +#] + +--- right-bracket-in-blocks --- +// Error: 3-4 unclosed delimiter +// Error: 6-7 unexpected closing bracket +// Hint: 6-7 try using a backslash escape: \] +[#{]}] + +// Error: 4-5 unexpected closing bracket +// Hint: 4-5 try using a backslash escape: \] +#[]] + +// Error: 4-5 unclosed delimiter +// Error: 7-8 unexpected closing bracket +// Hint: 7-8 try using a backslash escape: \] +#[#{]}] + +// Error: 2-3 unclosed delimiter +// Error: 3-4 unclosed delimiter +// Error: 4-5 unexpected closing bracket +// Hint: 4-5 try using a backslash escape: \] +#{{]}} + --- content-block-in-markup-scope --- // Content blocks also create a scope. #[#let x = 1] |
