From c1bb41dc9cadab596041d942bbaf7c7e8f8a14a5 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:23:56 -0300 Subject: [PATCH] new decorator syntax: '// @' --- crates/typst-syntax/src/kind.rs | 4 +- crates/typst-syntax/src/lexer.rs | 28 +++++-- tests/suite/syntax/decorator.typ | 124 +++++++++++++++++-------------- 3 files changed, 94 insertions(+), 62 deletions(-) diff --git a/crates/typst-syntax/src/kind.rs b/crates/typst-syntax/src/kind.rs index 8f0e806cd..fbe0b5028 100644 --- a/crates/typst-syntax/src/kind.rs +++ b/crates/typst-syntax/src/kind.rs @@ -13,7 +13,7 @@ pub enum SyntaxKind { LineComment, /// A block comment: `/* ... */`. BlockComment, - /// A decorator: `/! allow("warning")`. + /// A decorator: `// @allow("warning")`. Decorator, /// The contents of a file or content block. @@ -281,7 +281,7 @@ pub enum SyntaxKind { /// A destructuring assignment expression: `(x, y) = (1, 2)`. DestructAssignment, - /// A decorator's marker: `/!`. + /// A decorator's marker: `// @`. DecoratorMarker, /// A decorator's name: `allow`. DecoratorName, diff --git a/crates/typst-syntax/src/lexer.rs b/crates/typst-syntax/src/lexer.rs index 0e4b5b4be..fd057dc46 100644 --- a/crates/typst-syntax/src/lexer.rs +++ b/crates/typst-syntax/src/lexer.rs @@ -119,9 +119,10 @@ impl Lexer<'_> { let start = self.s.cursor(); let token = match self.s.eat() { Some(c) if is_space(c, self.mode) => self.whitespace(start, c), - Some('/') if self.s.eat_if('/') => self.line_comment(), + Some('/') if self.s.eat_if('/') => { + return self.line_comment_or_decorator(start); + } Some('/') if self.s.eat_if('*') => self.block_comment(), - Some('/') if self.s.eat_if('!') => return self.decorator(start), Some('*') if self.s.eat_if('/') => { let kind = self.error("unexpected end of block comment"); self.hint( @@ -184,9 +185,17 @@ impl Lexer<'_> { } } - fn line_comment(&mut self) -> SyntaxKind { + /// Parses a decorator if the line comment has the form + /// `// @something` + /// + /// Otherwise, parses a regular line comment. + fn line_comment_or_decorator(&mut self, start: usize) -> SyntaxNode { + self.s.eat_while(is_inline_whitespace); + if self.s.eat_if('@') { + return self.decorator(start); + } self.s.eat_until(is_newline); - SyntaxKind::LineComment + self.emit_token(SyntaxKind::LineComment, start) } fn block_comment(&mut self) -> SyntaxKind { @@ -274,7 +283,16 @@ impl Lexer<'_> { self.s.eat_while(is_inline_whitespace); SyntaxKind::Space } - Some('/') if self.s.eat_if('/') => self.line_comment(), + Some('/') if self.s.eat_if('/') => { + let node = self.line_comment_or_decorator(current_start); + if node.kind() == SyntaxKind::Decorator { + self.error("cannot have multiple decorators per line") + } else { + subtree.push(node); + current_start = self.s.cursor(); + continue; + } + } Some('/') if self.s.eat_if('*') => self.block_comment(), Some(_) if finished => { // After we finished specifying arguments, there must only diff --git a/tests/suite/syntax/decorator.typ b/tests/suite/syntax/decorator.typ index 7faa49a38..b1df81952 100644 --- a/tests/suite/syntax/decorator.typ +++ b/tests/suite/syntax/decorator.typ @@ -2,102 +2,115 @@ --- basic-decorators --- -/! allow() -/! allow("A") -/! allow("the") +// @allow() +// @allow("A") +// @allow("the") -/! allow("unnecessary-stars") +// @allow("unnecessary-stars") #h(0em) -#{ - /! allow("unnecessary-stars") - let _ = h(0em) +#let _ = { + // @allow("unnecessary-stars") + h(0em) } #let _ = $ - /! allow("unnecessary-stars") + // @allow("unnecessary-stars") h(#0em) $ --- decorator-comments --- -/! allow("abc") // this is ok +// @allow("abc") // this is ok -/! allow("abc") /* this is ok */ +// @allow("abc") /* this is ok */ -/! allow("abc" /* this is ok */, "abc") +// @allow("abc" /* this is ok */, "abc") -/! allow("abc" /* +// @allow("abc" /* this is ok */, "abc") --- decorator-strings --- -/! allow("@some/thing-there123") +// @allow("@some/thing-there123") --- unknown-decorator --- -// Error: 4-12 invalid decorator name -// Hint: 4-12 must be 'allow' -/! whatever() +// Error: 2:5-2:13 invalid decorator name +// Hint: 2:5-2:13 must be 'allow' + +// @whatever() --- invalid-decorator-syntax --- -// Error: 10-11 the character '*' is not valid in a decorator -/! allow(*) +// Error: 2:11-2:12 the character '*' is not valid in a decorator -// Error: 10-11 the character '5' is not valid in a decorator -/! allow(5) +// @allow(*) -// Error: 4-18 expected identifier -/! 555!**INVALID! +// Error: 2:11-2:12 the character '5' is not valid in a decorator -// Error: 9-12 expected opening paren -/! allow)") +// @allow(5) -// Error: 10-14 unclosed string -// Error: 14 expected closing paren -/! allow("abc +// Error: 2:5-2:19 expected identifier -// Error: 17-20 expected end of decorator -/! allow("abc") abc +// @555!**INVALID! -// Error: 16-21 expected comma -// Error: 23-26 expected end of decorator -/! allow("abc" "abc") abc +// Error: 2:10-2:13 expected opening paren -// Error: 16-21 expected comma -/! allow("abc" "abc", "abc") +// @allow)") -// Error: 10-11 unexpected comma -/! allow(, "abc", "abc", "abc") +// Error: 2:11-2:15 unclosed string +// Error: 2:15 expected closing paren + +// @allow("abc + +// Error: 2:18-2:21 expected end of decorator + +// @allow("abc") abc + +// Error: 2:17-2:22 expected comma +// Error: 2:24-2:27 expected end of decorator + +// @allow("abc" "abc") abc + +// Error: 2:17-2:22 expected comma + +// @allow("abc" "abc", "abc") + +// Error: 2:11-2:12 unexpected comma + +// @allow(, "abc", "abc", "abc") --- invalid-decorator-strings --- -// Error: 10-15 invalid character ' ' in a decorator's string -/! allow("a b") +// Error: 2:11-2:16 invalid character ' ' in a decorator's string -// Error: 10-18 invalid character '|' in a decorator's string -/! allow("aaaaa|") +// @allow("a b") + +// Error: 2:11-2:19 invalid character '|' in a decorator's string + +// @allow("aaaaa|") // TODO: Why does this print / instead of \? -// Error: 10-18 invalid character '/' in a decorator's string -/! allow("aaaaa\") +// Error: 2:11-2:19 invalid character '/' in a decorator's string + +// @allow("aaaaa\") --- allow-suppresses-warns-below --- -/! allow("unnecessary-stars") +// @allow("unnecessary-stars") #[**] -/! allow("unnecessary-stars") +// @allow("unnecessary-stars") #{ { [**] } } -/**/ /! allow("unnecessary-stars") +/**/ // @allow("unnecessary-stars") #[**] -/! allow("unnecessary-stars") +// @allow("unnecessary-stars") ** --- allow-suppresses-warn-with-tracepoint --- @@ -109,19 +122,20 @@ this is ok f() } -/! allow("unknown-font-families") +// @allow("unknown-font-families") #g() --- allow-suppresses-line-below-but-not-same-line --- // Warning: 3-5 no text within stars // Hint: 3-5 using multiple consecutive stars (e.g. **) has no additional effect -#[**] /! allow("unnecessary-stars") +#[**] // @allow("unnecessary-stars") #[**] --- allow-before-parbreak-doesnt-suppress-warn --- -// Warning: 3:3-3:5 no text within stars -// Hint: 3:3-3:5 using multiple consecutive stars (e.g. **) has no additional effect -/! allow("unnecessary-stars") +// Warning: 4:3-4:5 no text within stars +// Hint: 4:3-4:5 using multiple consecutive stars (e.g. **) has no additional effect + +// @allow("unnecessary-stars") #[**] @@ -129,7 +143,7 @@ this is ok // Warning: 4:4-4:6 no text within stars // Hint: 4:4-4:6 using multiple consecutive stars (e.g. **) has no additional effect #{ - /! allow("unnecessary-stars") + // @allow("unnecessary-stars") [**] } @@ -140,7 +154,7 @@ this is ok // Hint: 1-3 using multiple consecutive stars (e.g. **) has no additional effect ** -/! allow("unnecessary-stars") +// @allow("unnecessary-stars") #h(0em) // Warning: 3-5 no text within stars // Hint: 3-5 using multiple consecutive stars (e.g. **) has no additional effect @@ -152,10 +166,10 @@ this is ok text(font: "Unbeknownst")[] } -/! allow("unknown-font-families") +// @allow("unknown-font-families") #f() -/! allow("unknown-font-families") +// @allow("unknown-font-families") #context { text(font: "Unbeknownst")[] }