From 14dfb3f63004d06d4ba1c34a24fff6686b6e5312 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Mon, 24 Jun 2024 19:21:33 -0300 Subject: [PATCH] create DecoratorMarker and DecoratorName --- crates/typst-syntax/src/ast.rs | 18 +++++++++++- crates/typst-syntax/src/highlight.rs | 2 ++ crates/typst-syntax/src/kind.rs | 7 +++++ crates/typst-syntax/src/lexer.rs | 43 ++++++++++++++++++---------- tests/suite/syntax/decorator.typ | 3 +- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/crates/typst-syntax/src/ast.rs b/crates/typst-syntax/src/ast.rs index 8380ae1c6..019ff4663 100644 --- a/crates/typst-syntax/src/ast.rs +++ b/crates/typst-syntax/src/ast.rs @@ -80,6 +80,22 @@ impl<'a> Markup<'a> { } } +node! { + DecoratorName +} + +impl<'a> DecoratorName<'a> { + /// Get the decorator name. + pub fn get(self) -> &'a EcoString { + self.0.text() + } + + /// Get the decorator name as a string slice. + pub fn as_str(self) -> &'a str { + self.get() + } +} + node! { /// A decorator: `/! allow("warning")`. Decorator @@ -87,7 +103,7 @@ node! { impl<'a> Decorator<'a> { /// The name of the decorator, e.g. `allow`. - pub fn name(self) -> Ident<'a> { + pub fn name(self) -> DecoratorName<'a> { self.0.cast_first_match().unwrap_or_default() } diff --git a/crates/typst-syntax/src/highlight.rs b/crates/typst-syntax/src/highlight.rs index 83c15b14b..c78ec6941 100644 --- a/crates/typst-syntax/src/highlight.rs +++ b/crates/typst-syntax/src/highlight.rs @@ -287,6 +287,8 @@ pub fn highlight(node: &LinkedNode) -> Option { SyntaxKind::DestructAssignment => None, SyntaxKind::Decorator => None, + SyntaxKind::DecoratorMarker => None, + SyntaxKind::DecoratorName => None, SyntaxKind::LineComment => Some(Tag::Comment), SyntaxKind::BlockComment => Some(Tag::Comment), diff --git a/crates/typst-syntax/src/kind.rs b/crates/typst-syntax/src/kind.rs index 50d40960a..599806b57 100644 --- a/crates/typst-syntax/src/kind.rs +++ b/crates/typst-syntax/src/kind.rs @@ -280,6 +280,11 @@ pub enum SyntaxKind { Destructuring, /// A destructuring assignment expression: `(x, y) = (1, 2)`. DestructAssignment, + + /// A decorator's marker: `/!`. + DecoratorMarker, + /// A decorator's name: `allow`. + DecoratorName, } impl SyntaxKind { @@ -505,6 +510,8 @@ impl SyntaxKind { Self::FuncReturn => "`return` expression", Self::Destructuring => "destructuring pattern", Self::DestructAssignment => "destructuring assignment expression", + Self::DecoratorMarker => "decorator marker", + Self::DecoratorName => "decorator name", } } } diff --git a/crates/typst-syntax/src/lexer.rs b/crates/typst-syntax/src/lexer.rs index f8e8bbc21..9bfc64cb4 100644 --- a/crates/typst-syntax/src/lexer.rs +++ b/crates/typst-syntax/src/lexer.rs @@ -222,16 +222,18 @@ impl Lexer<'_> { /// `/! decorator-name("string argument1", "string argument2")` /// with optional whitespaces and comments between arguments. fn decorator(&mut self, start: usize) -> SyntaxNode { - // TODO: DecoratorMarker node - let current_start = start; - let mut subtree = vec![]; + // Start by lexing the marker. + let marker = self.emit_token(SyntaxKind::DecoratorMarker, start); + let mut subtree = vec![marker]; + + let current_start = self.s.cursor(); // Ignore initial non-newline whitespaces if !self.s.eat_while(is_inline_whitespace).is_empty() { subtree.push(self.emit_token(SyntaxKind::Space, current_start)); } - // Decorator's name + // Lex the decorator name let current_start = self.s.cursor(); if !self.s.eat_if(is_id_start) { self.s.eat_until(is_newline); @@ -241,17 +243,8 @@ impl Lexer<'_> { return SyntaxNode::inner(SyntaxKind::Decorator, subtree); } - self.s.eat_while(is_id_continue); - let ident = self.s.from(current_start); - - subtree.push(if ident == "allow" { - self.emit_token(SyntaxKind::Ident, current_start) - } else { - self.emit_error( - eco_format!("expected decorator name 'allow', found '{ident}'"), - current_start, - ) - }); + let decorator_name = self.decorator_name(current_start); + subtree.push(self.emit_token(decorator_name, current_start)); // Left parenthesis before decorator arguments let current_start = self.s.cursor(); @@ -323,6 +316,26 @@ impl Lexer<'_> { SyntaxNode::inner(SyntaxKind::Decorator, subtree) } + /// Lexes a decorator name. + /// A decorator name is an identifier within a specific subset of allowed + /// identifiers. + /// Currently, `allow` is the only valid decorator name. + fn decorator_name(&mut self, start: usize) -> SyntaxKind { + self.s.eat_while(is_id_continue); + let ident = self.s.from(start); + + if ident == "allow" { + SyntaxKind::DecoratorName + } else { + let error = self.error(eco_format!("invalid decorator name")); + self.hint("must be 'allow'"); + error + } + } + + /// Lexes a string in a decorator. + /// Currently, such strings only allow a very restricted set of characters. + /// These restrictions may be lifted in the future. fn decorator_string(&mut self) -> SyntaxKind { // TODO: Allow more characters in decorators' strings, perhaps allowing // newlines somehow. diff --git a/tests/suite/syntax/decorator.typ b/tests/suite/syntax/decorator.typ index e418eff08..d9de2b636 100644 --- a/tests/suite/syntax/decorator.typ +++ b/tests/suite/syntax/decorator.typ @@ -36,7 +36,8 @@ this is ok /! allow("@some/thing-there123") --- unknown-decorator --- -// Error: 4-12 expected decorator name 'allow', found 'whatever' +// Error: 4-12 invalid decorator name +// Hint: 4-12 must be 'allow' /! whatever() --- invalid-decorator-syntax ---