From ecf1f16c1665c78ac10847cc14559dd81ffcf2f7 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 24 Sep 2024 11:24:27 -0400 Subject: [PATCH] refactor(parser/html): refactor comments to be nodes in the tree (#4056) --- .../src/generated/node_factory.rs | 14 ++ .../src/generated/syntax_factory.rs | 33 +++++ crates/biome_html_formatter/src/generated.rs | 40 ++++++ .../src/html/any/element.rs | 1 + .../src/html/auxiliary/comment.rs | 10 ++ .../src/html/auxiliary/mod.rs | 1 + crates/biome_html_parser/src/lexer/mod.rs | 55 +++++--- crates/biome_html_parser/src/lexer/tests.rs | 28 ++++ crates/biome_html_parser/src/syntax/mod.rs | 12 ++ crates/biome_html_parser/src/token_source.rs | 2 + .../tests/html_specs/ok/comment.html.snap | 18 ++- .../biome_html_syntax/src/generated/kind.rs | 10 +- .../biome_html_syntax/src/generated/macros.rs | 4 + .../biome_html_syntax/src/generated/nodes.rs | 123 +++++++++++++++++- .../src/generated/nodes_mut.rs | 20 +++ crates/biome_html_syntax/src/lib.rs | 4 +- crates/biome_parser/src/parsed_syntax.rs | 2 +- xtask/codegen/html.ungram | 7 + xtask/codegen/src/html_kinds_src.rs | 4 +- xtask/codegen/src/js_kinds_src.rs | 2 + 20 files changed, 359 insertions(+), 31 deletions(-) create mode 100644 crates/biome_html_formatter/src/html/auxiliary/comment.rs diff --git a/crates/biome_html_factory/src/generated/node_factory.rs b/crates/biome_html_factory/src/generated/node_factory.rs index 1bd20e94154d..4c6dded8e642 100644 --- a/crates/biome_html_factory/src/generated/node_factory.rs +++ b/crates/biome_html_factory/src/generated/node_factory.rs @@ -61,6 +61,20 @@ pub fn html_closing_element( ], )) } +pub fn html_comment( + comment_start_token: SyntaxToken, + content_token: SyntaxToken, + comment_end_token: SyntaxToken, +) -> HtmlComment { + HtmlComment::unwrap_cast(SyntaxNode::new_detached( + HtmlSyntaxKind::HTML_COMMENT, + [ + Some(SyntaxElement::Token(comment_start_token)), + Some(SyntaxElement::Token(content_token)), + Some(SyntaxElement::Token(comment_end_token)), + ], + )) +} pub fn html_content(value_token: SyntaxToken) -> HtmlContent { HtmlContent::unwrap_cast(SyntaxNode::new_detached( HtmlSyntaxKind::HTML_CONTENT, diff --git a/crates/biome_html_factory/src/generated/syntax_factory.rs b/crates/biome_html_factory/src/generated/syntax_factory.rs index d0a9d80749e6..5ec99176c3b4 100644 --- a/crates/biome_html_factory/src/generated/syntax_factory.rs +++ b/crates/biome_html_factory/src/generated/syntax_factory.rs @@ -109,6 +109,39 @@ impl SyntaxFactory for HtmlSyntaxFactory { } slots.into_node(HTML_CLOSING_ELEMENT, children) } + HTML_COMMENT => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + HTML_COMMENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(HTML_COMMENT, children) + } HTML_CONTENT => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); diff --git a/crates/biome_html_formatter/src/generated.rs b/crates/biome_html_formatter/src/generated.rs index d2c5baa049b4..9d235e3610de 100644 --- a/crates/biome_html_formatter/src/generated.rs +++ b/crates/biome_html_formatter/src/generated.rs @@ -118,6 +118,46 @@ impl IntoFormat for biome_html_syntax::HtmlClosingElement { ) } } +impl FormatRule + for crate::html::auxiliary::comment::FormatHtmlComment +{ + type Context = HtmlFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_html_syntax::HtmlComment, + f: &mut HtmlFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_html_syntax::HtmlComment { + type Format<'a> = FormatRefWithRule< + 'a, + biome_html_syntax::HtmlComment, + crate::html::auxiliary::comment::FormatHtmlComment, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::html::auxiliary::comment::FormatHtmlComment::default(), + ) + } +} +impl IntoFormat for biome_html_syntax::HtmlComment { + type Format = FormatOwnedWithRule< + biome_html_syntax::HtmlComment, + crate::html::auxiliary::comment::FormatHtmlComment, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::html::auxiliary::comment::FormatHtmlComment::default(), + ) + } +} impl FormatRule for crate::html::auxiliary::content::FormatHtmlContent { diff --git a/crates/biome_html_formatter/src/html/any/element.rs b/crates/biome_html_formatter/src/html/any/element.rs index fb8122234769..a3a8862826c4 100644 --- a/crates/biome_html_formatter/src/html/any/element.rs +++ b/crates/biome_html_formatter/src/html/any/element.rs @@ -9,6 +9,7 @@ impl FormatRule for FormatAnyHtmlElement { fn fmt(&self, node: &AnyHtmlElement, f: &mut HtmlFormatter) -> FormatResult<()> { match node { AnyHtmlElement::HtmlBogusElement(node) => node.format().fmt(f), + AnyHtmlElement::HtmlComment(node) => node.format().fmt(f), AnyHtmlElement::HtmlContent(node) => node.format().fmt(f), AnyHtmlElement::HtmlElement(node) => node.format().fmt(f), AnyHtmlElement::HtmlSelfClosingElement(node) => node.format().fmt(f), diff --git a/crates/biome_html_formatter/src/html/auxiliary/comment.rs b/crates/biome_html_formatter/src/html/auxiliary/comment.rs new file mode 100644 index 000000000000..a793f4946f53 --- /dev/null +++ b/crates/biome_html_formatter/src/html/auxiliary/comment.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use biome_html_syntax::HtmlComment; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatHtmlComment; +impl FormatNodeRule for FormatHtmlComment { + fn fmt_fields(&self, node: &HtmlComment, f: &mut HtmlFormatter) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_html_formatter/src/html/auxiliary/mod.rs b/crates/biome_html_formatter/src/html/auxiliary/mod.rs index f0d3122e674e..021b788091a7 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/mod.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod attribute; pub(crate) mod attribute_initializer_clause; pub(crate) mod closing_element; +pub(crate) mod comment; pub(crate) mod content; pub(crate) mod directive; pub(crate) mod element; diff --git a/crates/biome_html_parser/src/lexer/mod.rs b/crates/biome_html_parser/src/lexer/mod.rs index e7adc8935742..1fc9dcb9f6d2 100644 --- a/crates/biome_html_parser/src/lexer/mod.rs +++ b/crates/biome_html_parser/src/lexer/mod.rs @@ -2,8 +2,8 @@ mod tests; use crate::token_source::{HtmlEmbededLanguage, HtmlLexContext}; use biome_html_syntax::HtmlSyntaxKind::{ - COMMENT, DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL, NEWLINE, - TOMBSTONE, UNICODE_BOM, WHITESPACE, + DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL, NEWLINE, TOMBSTONE, + UNICODE_BOM, WHITESPACE, }; use biome_html_syntax::{HtmlSyntaxKind, TextLen, TextSize, T}; use biome_parser::diagnostic::ParseDiagnostic; @@ -137,6 +137,24 @@ impl<'src> HtmlLexer<'src> { } } + /// Consume a token in the [HtmlLexContext::Comment] context. + fn consume_inside_comment(&mut self, current: u8) -> HtmlSyntaxKind { + match current { + b'<' if self.at_start_comment() => self.consume_comment_start(), + b'-' if self.at_end_comment() => self.consume_comment_end(), + _ => { + while let Some(char) = self.current_byte() { + if self.at_end_comment() { + // eat --> + break; + } + self.advance_byte_or_char(char); + } + HTML_LITERAL + } + } + } + /// Bumps the current byte and creates a lexed token of the passed in kind. #[inline] fn consume_byte(&mut self, tok: HtmlSyntaxKind) -> HtmlSyntaxKind { @@ -309,28 +327,12 @@ impl<'src> HtmlLexer<'src> { self.assert_byte(b'<'); if self.at_start_comment() { - self.consume_comment() + self.consume_comment_start() } else { self.consume_byte(T![<]) } } - fn consume_comment(&mut self) -> HtmlSyntaxKind { - // eat - self.advance(3); - return COMMENT; - } - self.advance_byte_or_char(char); - } - - COMMENT - } - fn at_start_comment(&mut self) -> bool { self.current_byte() == Some(b'<') && self.byte_at(1) == Some(b'!') @@ -344,6 +346,20 @@ impl<'src> HtmlLexer<'src> { && self.byte_at(2) == Some(b'>') } + fn consume_comment_start(&mut self) -> HtmlSyntaxKind { + debug_assert!(self.at_start_comment()); + + self.advance(4); + T![] + } + /// Lexes a `\u0000` escape sequence. Assumes that the lexer is positioned at the `u` token. /// /// A unicode escape sequence must consist of 4 hex characters. @@ -462,6 +478,7 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> { HtmlLexContext::EmbeddedLanguage(lang) => { self.consume_token_embedded_language(current, lang) } + HtmlLexContext::Comment => self.consume_inside_comment(current), }, None => EOF, } diff --git a/crates/biome_html_parser/src/lexer/tests.rs b/crates/biome_html_parser/src/lexer/tests.rs index a541a6e6ec2e..84a64fc5e2bd 100644 --- a/crates/biome_html_parser/src/lexer/tests.rs +++ b/crates/biome_html_parser/src/lexer/tests.rs @@ -289,3 +289,31 @@ fn unquoted_attribute_value_invalid_chars() { ERROR_TOKEN: 3, } } + +#[test] +fn comment_start() { + assert_lex! { + "", + COMMENT_END: 3, + } +} + +#[test] +fn comment_full() { + assert_lex! { + HtmlLexContext::Comment, + "", + COMMENT_START: 4, + HTML_LITERAL: 5, + COMMENT_END: 3, + } +} diff --git a/crates/biome_html_parser/src/syntax/mod.rs b/crates/biome_html_parser/src/syntax/mod.rs index 4a446710e888..617883c47242 100644 --- a/crates/biome_html_parser/src/syntax/mod.rs +++ b/crates/biome_html_parser/src/syntax/mod.rs @@ -158,6 +158,7 @@ impl ParseNodeList for ElementList { fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { match p.cur() { + T![]); + Present(m.complete(p, HTML_COMMENT)) +} diff --git a/crates/biome_html_parser/src/token_source.rs b/crates/biome_html_parser/src/token_source.rs index beb58b5c5df2..d5a80699fa50 100644 --- a/crates/biome_html_parser/src/token_source.rs +++ b/crates/biome_html_parser/src/token_source.rs @@ -33,6 +33,8 @@ pub(crate) enum HtmlLexContext { Doctype, /// Treat everything as text until the closing tag is encountered. EmbeddedLanguage(HtmlEmbededLanguage), + /// Comments are treated as text until the closing comment tag is encountered. + Comment, } #[derive(Copy, Clone, Debug)] diff --git a/crates/biome_html_parser/tests/html_specs/ok/comment.html.snap b/crates/biome_html_parser/tests/html_specs/ok/comment.html.snap index 52f3ed2f5fa6..f1dfd8f85bc8 100644 --- a/crates/biome_html_parser/tests/html_specs/ok/comment.html.snap +++ b/crates/biome_html_parser/tests/html_specs/ok/comment.html.snap @@ -16,8 +16,14 @@ expression: snapshot HtmlRoot { bom_token: missing (optional), directive: missing (optional), - html: HtmlElementList [], - eof_token: EOF@0..21 "" [Comments(""), Newline("\n")] [], + html: HtmlElementList [ + HtmlComment { + comment_start_token: COMMENT_START@0..4 "" [] [], + }, + ], + eof_token: EOF@20..21 "" [Newline("\n")] [], } ``` @@ -27,7 +33,11 @@ HtmlRoot { 0: HTML_ROOT@0..21 0: (empty) 1: (empty) - 2: HTML_ELEMENT_LIST@0..0 - 3: EOF@0..21 "" [Comments(""), Newline("\n")] [] + 2: HTML_ELEMENT_LIST@0..20 + 0: HTML_COMMENT@0..20 + 0: COMMENT_START@0..4 "" [] [] + 3: EOF@20..21 "" [Newline("\n")] [] ``` diff --git a/crates/biome_html_syntax/src/generated/kind.rs b/crates/biome_html_syntax/src/generated/kind.rs index 2793fcfbf00a..85f0386e7711 100644 --- a/crates/biome_html_syntax/src/generated/kind.rs +++ b/crates/biome_html_syntax/src/generated/kind.rs @@ -19,6 +19,8 @@ pub enum HtmlSyntaxKind { EQ, BANG, MINUS, + COMMENT_START, + COMMENT_END, NULL_KW, TRUE_KW, FALSE_KW, @@ -30,7 +32,6 @@ pub enum HtmlSyntaxKind { NEWLINE, WHITESPACE, IDENT, - COMMENT, HTML_IDENT, HTML_ROOT, HTML_DIRECTIVE, @@ -46,6 +47,7 @@ pub enum HtmlSyntaxKind { HTML_ELEMENT_LIST, HTML_ATTRIBUTE_LIST, HTML_CONTENT, + HTML_COMMENT, HTML_BOGUS, HTML_BOGUS_ELEMENT, HTML_BOGUS_ATTRIBUTE, @@ -56,7 +58,7 @@ use self::HtmlSyntaxKind::*; impl HtmlSyntaxKind { pub const fn is_punct(self) -> bool { match self { - L_ANGLE | R_ANGLE | SLASH | EQ | BANG | MINUS => true, + L_ANGLE | R_ANGLE | SLASH | EQ | BANG | MINUS | COMMENT_START | COMMENT_END => true, _ => false, } } @@ -91,6 +93,8 @@ impl HtmlSyntaxKind { EQ => "=", BANG => "!", MINUS => "-", + COMMENT_START => "", NULL_KW => "null", TRUE_KW => "true", FALSE_KW => "false", @@ -104,4 +108,4 @@ impl HtmlSyntaxKind { } #[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] #[macro_export] -macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; } +macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [] => { $ crate :: HtmlSyntaxKind :: COMMENT_END } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; } diff --git a/crates/biome_html_syntax/src/generated/macros.rs b/crates/biome_html_syntax/src/generated/macros.rs index d0cfe8761f8d..20801883a1a6 100644 --- a/crates/biome_html_syntax/src/generated/macros.rs +++ b/crates/biome_html_syntax/src/generated/macros.rs @@ -29,6 +29,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::HtmlClosingElement::new_unchecked(node) }; $body } + $crate::HtmlSyntaxKind::HTML_COMMENT => { + let $pattern = unsafe { $crate::HtmlComment::new_unchecked(node) }; + $body + } $crate::HtmlSyntaxKind::HTML_CONTENT => { let $pattern = unsafe { $crate::HtmlContent::new_unchecked(node) }; $body diff --git a/crates/biome_html_syntax/src/generated/nodes.rs b/crates/biome_html_syntax/src/generated/nodes.rs index ac5c04959d18..db56ccd46dd7 100644 --- a/crates/biome_html_syntax/src/generated/nodes.rs +++ b/crates/biome_html_syntax/src/generated/nodes.rs @@ -153,6 +153,51 @@ pub struct HtmlClosingElementFields { pub r_angle_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct HtmlComment { + pub(crate) syntax: SyntaxNode, +} +impl HtmlComment { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> HtmlCommentFields { + HtmlCommentFields { + comment_start_token: self.comment_start_token(), + content_token: self.content_token(), + comment_end_token: self.comment_end_token(), + } + } + pub fn comment_start_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn content_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn comment_end_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for HtmlComment { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct HtmlCommentFields { + pub comment_start_token: SyntaxResult, + pub content_token: SyntaxResult, + pub comment_end_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct HtmlContent { pub(crate) syntax: SyntaxNode, } @@ -549,6 +594,7 @@ impl AnyHtmlAttribute { #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyHtmlElement { HtmlBogusElement(HtmlBogusElement), + HtmlComment(HtmlComment), HtmlContent(HtmlContent), HtmlElement(HtmlElement), HtmlSelfClosingElement(HtmlSelfClosingElement), @@ -560,6 +606,12 @@ impl AnyHtmlElement { _ => None, } } + pub fn as_html_comment(&self) -> Option<&HtmlComment> { + match &self { + AnyHtmlElement::HtmlComment(item) => Some(item), + _ => None, + } + } pub fn as_html_content(&self) -> Option<&HtmlContent> { match &self { AnyHtmlElement::HtmlContent(item) => Some(item), @@ -710,6 +762,55 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for HtmlComment { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(HTML_COMMENT as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == HTML_COMMENT + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for HtmlComment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HtmlComment") + .field( + "comment_start_token", + &support::DebugSyntaxResult(self.comment_start_token()), + ) + .field( + "content_token", + &support::DebugSyntaxResult(self.content_token()), + ) + .field( + "comment_end_token", + &support::DebugSyntaxResult(self.comment_end_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: HtmlComment) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: HtmlComment) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for HtmlContent { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -1157,6 +1258,11 @@ impl From for AnyHtmlElement { AnyHtmlElement::HtmlBogusElement(node) } } +impl From for AnyHtmlElement { + fn from(node: HtmlComment) -> AnyHtmlElement { + AnyHtmlElement::HtmlComment(node) + } +} impl From for AnyHtmlElement { fn from(node: HtmlContent) -> AnyHtmlElement { AnyHtmlElement::HtmlContent(node) @@ -1175,18 +1281,24 @@ impl From for AnyHtmlElement { impl AstNode for AnyHtmlElement { type Language = Language; const KIND_SET: SyntaxKindSet = HtmlBogusElement::KIND_SET + .union(HtmlComment::KIND_SET) .union(HtmlContent::KIND_SET) .union(HtmlElement::KIND_SET) .union(HtmlSelfClosingElement::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - HTML_BOGUS_ELEMENT | HTML_CONTENT | HTML_ELEMENT | HTML_SELF_CLOSING_ELEMENT + HTML_BOGUS_ELEMENT + | HTML_COMMENT + | HTML_CONTENT + | HTML_ELEMENT + | HTML_SELF_CLOSING_ELEMENT ) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { HTML_BOGUS_ELEMENT => AnyHtmlElement::HtmlBogusElement(HtmlBogusElement { syntax }), + HTML_COMMENT => AnyHtmlElement::HtmlComment(HtmlComment { syntax }), HTML_CONTENT => AnyHtmlElement::HtmlContent(HtmlContent { syntax }), HTML_ELEMENT => AnyHtmlElement::HtmlElement(HtmlElement { syntax }), HTML_SELF_CLOSING_ELEMENT => { @@ -1199,6 +1311,7 @@ impl AstNode for AnyHtmlElement { fn syntax(&self) -> &SyntaxNode { match self { AnyHtmlElement::HtmlBogusElement(it) => &it.syntax, + AnyHtmlElement::HtmlComment(it) => &it.syntax, AnyHtmlElement::HtmlContent(it) => &it.syntax, AnyHtmlElement::HtmlElement(it) => &it.syntax, AnyHtmlElement::HtmlSelfClosingElement(it) => &it.syntax, @@ -1207,6 +1320,7 @@ impl AstNode for AnyHtmlElement { fn into_syntax(self) -> SyntaxNode { match self { AnyHtmlElement::HtmlBogusElement(it) => it.syntax, + AnyHtmlElement::HtmlComment(it) => it.syntax, AnyHtmlElement::HtmlContent(it) => it.syntax, AnyHtmlElement::HtmlElement(it) => it.syntax, AnyHtmlElement::HtmlSelfClosingElement(it) => it.syntax, @@ -1217,6 +1331,7 @@ impl std::fmt::Debug for AnyHtmlElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AnyHtmlElement::HtmlBogusElement(it) => std::fmt::Debug::fmt(it, f), + AnyHtmlElement::HtmlComment(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlContent(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlElement(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlSelfClosingElement(it) => std::fmt::Debug::fmt(it, f), @@ -1227,6 +1342,7 @@ impl From for SyntaxNode { fn from(n: AnyHtmlElement) -> SyntaxNode { match n { AnyHtmlElement::HtmlBogusElement(it) => it.into(), + AnyHtmlElement::HtmlComment(it) => it.into(), AnyHtmlElement::HtmlContent(it) => it.into(), AnyHtmlElement::HtmlElement(it) => it.into(), AnyHtmlElement::HtmlSelfClosingElement(it) => it.into(), @@ -1264,6 +1380,11 @@ impl std::fmt::Display for HtmlClosingElement { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for HtmlComment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for HtmlContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/biome_html_syntax/src/generated/nodes_mut.rs b/crates/biome_html_syntax/src/generated/nodes_mut.rs index 5094ee5a0d9a..fb793fd99740 100644 --- a/crates/biome_html_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_html_syntax/src/generated/nodes_mut.rs @@ -57,6 +57,26 @@ impl HtmlClosingElement { ) } } +impl HtmlComment { + pub fn with_comment_start_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_content_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_comment_end_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} impl HtmlContent { pub fn with_value_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/crates/biome_html_syntax/src/lib.rs b/crates/biome_html_syntax/src/lib.rs index 460f975b0be0..076c5987515e 100644 --- a/crates/biome_html_syntax/src/lib.rs +++ b/crates/biome_html_syntax/src/lib.rs @@ -28,7 +28,7 @@ impl From for u16 { impl HtmlSyntaxKind { pub fn is_comments(self) -> bool { - matches!(self, HtmlSyntaxKind::COMMENT) + matches!(self, HtmlSyntaxKind::HTML_COMMENT) } #[inline] @@ -99,7 +99,7 @@ impl TryFrom for TriviaPieceKind { } } else if value.is_comments() { match value { - HtmlSyntaxKind::COMMENT => Ok(TriviaPieceKind::SingleLineComment), + HtmlSyntaxKind::HTML_COMMENT => Ok(TriviaPieceKind::SingleLineComment), _ => unreachable!("Not Comment"), } } else { diff --git a/crates/biome_parser/src/parsed_syntax.rs b/crates/biome_parser/src/parsed_syntax.rs index e8f4454b7082..0aa016630fd6 100644 --- a/crates/biome_parser/src/parsed_syntax.rs +++ b/crates/biome_parser/src/parsed_syntax.rs @@ -61,7 +61,7 @@ impl ParsedSyntax { } } - /// Calls `op` if the syntax is absent ond otherwise returns [ParsedSyntax::Present] + /// Calls `op` if the syntax is absent and otherwise returns [ParsedSyntax::Present] #[inline] pub fn or_else(self, op: F) -> ParsedSyntax where diff --git a/xtask/codegen/html.ungram b/xtask/codegen/html.ungram index 29e1e83e4750..2c2976cf80f2 100644 --- a/xtask/codegen/html.ungram +++ b/xtask/codegen/html.ungram @@ -68,6 +68,7 @@ AnyHtmlElement = HtmlSelfClosingElement | HtmlElement | HtmlContent + | HtmlComment | HtmlBogusElement @@ -100,6 +101,12 @@ HtmlClosingElement = name: HtmlName '>' +// +HtmlComment = + '' + // ================================== // Attributes // ================================== diff --git a/xtask/codegen/src/html_kinds_src.rs b/xtask/codegen/src/html_kinds_src.rs index 9dc37349f894..ac14e54acf87 100644 --- a/xtask/codegen/src/html_kinds_src.rs +++ b/xtask/codegen/src/html_kinds_src.rs @@ -8,6 +8,8 @@ pub const HTML_KINDS_SRC: KindsSrc = KindsSrc { ("=", "EQ"), ("!", "BANG"), ("-", "MINUS"), + ("", "COMMENT_END"), ], keywords: &["null", "true", "false", "doctype", "html"], literals: &["HTML_STRING_LITERAL", "HTML_LITERAL"], @@ -16,7 +18,6 @@ pub const HTML_KINDS_SRC: KindsSrc = KindsSrc { "NEWLINE", "WHITESPACE", "IDENT", - "COMMENT", "HTML_IDENT", ], nodes: &[ @@ -34,6 +35,7 @@ pub const HTML_KINDS_SRC: KindsSrc = KindsSrc { "HTML_ELEMENT_LIST", "HTML_ATTRIBUTE_LIST", "HTML_CONTENT", + "HTML_COMMENT", // Bogus nodes "HTML_BOGUS", "HTML_BOGUS_ELEMENT", diff --git a/xtask/codegen/src/js_kinds_src.rs b/xtask/codegen/src/js_kinds_src.rs index 90568ccb86ee..09f05a304392 100644 --- a/xtask/codegen/src/js_kinds_src.rs +++ b/xtask/codegen/src/js_kinds_src.rs @@ -688,6 +688,8 @@ impl Field { ("~=", _) => "whitespace_like", (",", _) => "comma", ("---", LanguageKind::Yaml) => "dashdashdash", + ("", LanguageKind::Html) => "comment_end", _ => name, };