diff --git a/crates/biome_formatter/src/printed_tokens.rs b/crates/biome_formatter/src/printed_tokens.rs index 16fd683d8df6..c2eab266e918 100644 --- a/crates/biome_formatter/src/printed_tokens.rs +++ b/crates/biome_formatter/src/printed_tokens.rs @@ -31,7 +31,10 @@ impl PrintedTokens { let range = token.text_trimmed_range(); if !self.offsets.insert(range.start()) { - panic!("You tried to print the token '{token:?}' twice, and this is not valid."); + panic!( + "You tried to print the token '{token:?}' twice, and this is not valid.\ + \nYou may need to memoize the token if you are writing it to multiple buffers at the same time." + ); } } diff --git a/crates/biome_html_factory/src/generated/node_factory.rs b/crates/biome_html_factory/src/generated/node_factory.rs index 622e3deef222..070fd0d1cbcb 100644 --- a/crates/biome_html_factory/src/generated/node_factory.rs +++ b/crates/biome_html_factory/src/generated/node_factory.rs @@ -111,20 +111,6 @@ 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 20cb1c4d84cf..1a51134e4ea1 100644 --- a/crates/biome_html_factory/src/generated/syntax_factory.rs +++ b/crates/biome_html_factory/src/generated/syntax_factory.rs @@ -194,39 +194,6 @@ 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/comments.rs b/crates/biome_html_formatter/src/comments.rs index 9a31af3640a0..9476a8dece30 100644 --- a/crates/biome_html_formatter/src/comments.rs +++ b/crates/biome_html_formatter/src/comments.rs @@ -7,8 +7,8 @@ use biome_formatter::{ prelude::*, write, }; -use biome_html_syntax::HtmlLanguage; -use biome_rowan::SyntaxTriviaPieceComments; +use biome_html_syntax::{HtmlClosingElement, HtmlLanguage, HtmlOpeningElement, HtmlSyntaxKind}; +use biome_rowan::{SyntaxNodeCast, SyntaxTriviaPieceComments}; use biome_suppression::parse_suppression_comment; use crate::context::HtmlFormatContext; @@ -44,13 +44,84 @@ impl CommentStyle for HtmlCommentStyle { } fn get_comment_kind(_comment: &SyntaxTriviaPieceComments) -> CommentKind { - CommentKind::Block + CommentKind::Line } + /// This allows us to override which comments are associated with which nodes. + /// + /// While every comment is directly attached to a **syntax token**, Biome actually builds a map of comments to **syntax nodes** separately. This map lives in [`HtmlComments`]. This is so that we can easily look up comments that are associated with a specific node. It's part of how suppression comments are handled. + /// + /// This method specifically, however, lets us fine tune which comments are associated with which nodes. This is useful when the default heuristic fails. fn place_comment( &self, comment: DecoratedComment, ) -> CommentPlacement { + // Fix trailing comments that are right before EOF being assigned to the wrong node. + // + // The issue is demonstrated in the example below. + // ```html + // Foo + // + // + // ``` + if let Some(token) = comment.following_token() { + if token.kind() == HtmlSyntaxKind::EOF { + return CommentPlacement::trailing(comment.enclosing_node().clone(), comment); + } + } + + // Fix trailing comments that should actually be leading comments for the next node. + // ```html + // 123456 + // ``` + // This fix will ensure that the ignore comment is assigned to the 456 node instead of the 123 node. + if let Some(following_node) = comment.following_node() { + if comment.text_position().is_same_line() { + return CommentPlacement::leading(following_node.clone(), comment); + } + } + // match (comment.preceding_node(), comment.following_node()) { + // (Some(preceding_node), Some(following_node)) => { + // if preceding_node.kind() == HtmlSyntaxKind::HTML_CONTENT + // && following_node.kind() == HtmlSyntaxKind::HTML_CONTENT + // { + // return CommentPlacement::leading(following_node.clone(), comment); + // } + + // if matches!( + // following_node.kind(), + // HtmlSyntaxKind::HTML_CONTENT + // | HtmlSyntaxKind::HTML_ELEMENT + // | HtmlSyntaxKind::HTML_SELF_CLOSING_ELEMENT + // | HtmlSyntaxKind::HTML_BOGUS_ELEMENT + // ) { + // return CommentPlacement::leading(following_node.clone(), comment); + // } + // } + // _ => {} + // } + + // move leading comments placed on closing tags to trailing tags of previous siblings, or to be dangling if no siblings are present. + if let Some(_closing_tag) = comment + .following_node() + .and_then(|node| node.clone().cast::()) + { + if let Some(_preceding_opening_tag) = comment + .preceding_node() + .and_then(|node| node.clone().cast::()) + { + return CommentPlacement::dangling( + comment.preceding_node().unwrap().clone(), + comment, + ); + } else { + return CommentPlacement::trailing( + comment.preceding_node().unwrap().clone(), + comment, + ); + } + } + CommentPlacement::Default(comment) } } diff --git a/crates/biome_html_formatter/src/generated.rs b/crates/biome_html_formatter/src/generated.rs index 619c7583b827..b1dff930364d 100644 --- a/crates/biome_html_formatter/src/generated.rs +++ b/crates/biome_html_formatter/src/generated.rs @@ -228,44 +228,6 @@ 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<'_> { - 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 { - 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 32bfcff44eff..c316fd0cd047 100644 --- a/crates/biome_html_formatter/src/html/any/element.rs +++ b/crates/biome_html_formatter/src/html/any/element.rs @@ -11,7 +11,6 @@ impl FormatRule for FormatAnyHtmlElement { AnyHtmlElement::AnyHtmlContent(node) => node.format().fmt(f), AnyHtmlElement::HtmlBogusElement(node) => node.format().fmt(f), AnyHtmlElement::HtmlCdataSection(node) => node.format().fmt(f), - AnyHtmlElement::HtmlComment(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 deleted file mode 100644 index 59d2c29f086c..000000000000 --- a/crates/biome_html_formatter/src/html/auxiliary/comment.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::prelude::*; -use crate::verbatim::format_html_verbatim_node; -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_html_verbatim_node(node.syntax()).fmt(f) - } -} diff --git a/crates/biome_html_formatter/src/html/auxiliary/element.rs b/crates/biome_html_formatter/src/html/auxiliary/element.rs index 68eb62e7f292..02492c6d913f 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/element.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/element.rs @@ -4,7 +4,9 @@ use crate::{ html::lists::element_list::{FormatChildrenResult, FormatHtmlElementList}, prelude::*, }; -use biome_formatter::{FormatRuleWithOptions, format_args, write}; +use biome_formatter::{ + CstFormatContext, FormatRefWithRule, FormatRuleWithOptions, format_args, write, +}; use biome_html_syntax::{HtmlElement, HtmlElementFields}; use super::{ @@ -155,4 +157,28 @@ impl FormatNodeRule for FormatHtmlElement { Ok(()) } + + fn fmt_trailing_comments(&self, node: &HtmlElement, f: &mut HtmlFormatter) -> FormatResult<()> { + // If there is leading whitespace before a leading comment, we need to preserve it because it's probably indentation. + // See prettier test case: crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html + // The current implementation for `biome_formatter::FormatTrailingComments` actually has a ton of js specific behavior that we don't want in the html formatter. + + let comments = f.context().comments().clone(); + let trailing_comments = comments.trailing_comments(node.syntax()); + for comment in trailing_comments { + let format_comment = FormatRefWithRule::new( + comment, + ::CommentRule::default(), + ); + match comment.lines_before() { + 0 => {} + 1 => write!(f, [hard_line_break()])?, + _ => write!(f, [empty_line()])?, + } + write!(f, [format_comment])?; + comment.mark_formatted(); + } + + Ok(()) + } } diff --git a/crates/biome_html_formatter/src/html/auxiliary/mod.rs b/crates/biome_html_formatter/src/html/auxiliary/mod.rs index acd1b0f5b089..f79b0c3b2f17 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/mod.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/mod.rs @@ -5,7 +5,6 @@ pub(crate) mod attribute_initializer_clause; pub(crate) mod attribute_name; pub(crate) mod cdata_section; 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_formatter/src/html/auxiliary/opening_element.rs b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs index 1559cfd872fb..9099e3b8d0d2 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs @@ -45,11 +45,19 @@ impl FormatNodeRule for FormatHtmlOpeningElement { r_angle_token, } = node.as_fields(); + let l_angle_token = l_angle_token?; let name = name?; let is_whitespace_sensitive = is_element_whitespace_sensitive(f, &name); let is_canonical_html_tag = is_canonical_html_tag(&name); let bracket_same_line = f.options().bracket_same_line().value(); + + // if this isn't whitespace sensitive, and there is a comment trivia + // we must add a newline right after the comment. + if !is_whitespace_sensitive && l_angle_token.has_leading_comments() { + write!(f, [hard_line_break()])?; + } + write!(f, [l_angle_token.format(), name.format()])?; write!( diff --git a/crates/biome_html_formatter/src/html/auxiliary/root.rs b/crates/biome_html_formatter/src/html/auxiliary/root.rs index 2455a383826a..b12c8ebf4e14 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/root.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/root.rs @@ -27,10 +27,7 @@ impl FormatNodeRule for FormatHtmlRoot { html.format().fmt(f)?; - if let Ok(eof) = eof_token { - eof.format().fmt(f)?; - } - write!(f, [hard_line_break()])?; + write!(f, [hard_line_break(), format_removed(&eof_token?)])?; Ok(()) } diff --git a/crates/biome_html_formatter/src/html/lists/element_list.rs b/crates/biome_html_formatter/src/html/lists/element_list.rs index d79ee41daef4..bc256f86fd52 100644 --- a/crates/biome_html_formatter/src/html/lists/element_list.rs +++ b/crates/biome_html_formatter/src/html/lists/element_list.rs @@ -62,15 +62,17 @@ impl FormatRule for FormatHtmlElementList { let result = self.fmt_children(node, f)?; match result { FormatChildrenResult::ForceMultiline(format_multiline) => { - write!(f, [format_multiline]) + write!(f, [format_multiline])?; } FormatChildrenResult::BestFitting { flat_children, expanded_children, } => { - write!(f, [best_fitting![flat_children, expanded_children]]) + write!(f, [best_fitting![flat_children, expanded_children]])?; } } + + Ok(()) } } @@ -113,8 +115,6 @@ impl FormatHtmlElementList { list: &HtmlElementList, f: &mut HtmlFormatter, ) -> FormatResult { - self.disarm_debug_assertions(list, f); - let borrowed_opening_r_angle = self .borrowed_tokens .borrowed_opening_r_angle @@ -147,7 +147,7 @@ impl FormatHtmlElementList { let mut force_multiline = layout.is_multiline(); - let mut children = html_split_children(list.iter(), f.context().comments())?; + let mut children = html_split_children(list.iter(), f)?; // Trim trailing new lines if let Some(HtmlChild::EmptyLine | HtmlChild::Newline) = children.last() { @@ -181,6 +181,13 @@ impl FormatHtmlElementList { Some(WordSeparator::BetweenWords) } + Some(HtmlChild::Comment(_)) => { + // Some(WordSeparator::BetweenElements) + None + // FIXME: probably not correct behavior here + // Some(WordSeparator::Lines(2)) + } + // Last word or last word before an element without any whitespace in between Some(HtmlChild::NonText(next_child)) => Some(WordSeparator::EndOfText { is_soft_line_break: !matches!( @@ -191,6 +198,8 @@ impl FormatHtmlElementList { is_element_whitespace_sensitive_from_element(f, next_child), }), + Some(HtmlChild::Verbatim(_)) => None, + Some(HtmlChild::Newline | HtmlChild::Whitespace | HtmlChild::EmptyLine) => { None } @@ -210,6 +219,52 @@ impl FormatHtmlElementList { } } + HtmlChild::Comment(comment) => { + let memoized = comment.memoized(); + flat.write(&memoized, f); + multiline.write_content(&memoized, f); + } + + HtmlChild::Verbatim(element) => { + // If the verbatim element is multiline, we need to force multiline mode + if element.syntax().text_with_trivia().contains_char('\n') { + force_multiline = true; + } + + // HACK: We need the condition on formatting comments because otherwise comments will get printed twice. + let should_format_comments = if let AnyHtmlElement::AnyHtmlContent(content) = + element + { + let mut has_newline_after_comment = false; + if let Some(first_token) = content.syntax().first_token() { + let mut trivia_iter = first_token.leading_trivia().pieces().peekable(); + while let (Some(current), Some(next)) = + (trivia_iter.next(), trivia_iter.peek()) + { + if current.is_comments() && next.is_newline() { + has_newline_after_comment = true; + break; + } + } + } + + has_newline_after_comment + } else { + true + }; + if force_multiline { + let content = format_suppressed_node(element.syntax()) + .with_format_comments(should_format_comments); + multiline.write_content(&content, f); + } else { + let memoized = format_suppressed_node(element.syntax()) + .with_format_comments(should_format_comments) + .memoized(); + flat.write(&memoized, f); + multiline.write_content(&memoized, f); + } + } + // * Whitespace after the opening tag and before a meaningful text: `
a` // * Whitespace before the closing tag: `a
` // * Whitespace before an opening tag: `a
` @@ -315,8 +370,12 @@ impl FormatHtmlElementList { } } + Some(HtmlChild::Comment(_)) => Some(LineMode::Hard), + // Add a hard line break if what comes after the element is not a text or is all whitespace - Some(HtmlChild::NonText(next_non_text)) => { + Some( + HtmlChild::NonText(next_non_text) | HtmlChild::Verbatim(next_non_text), + ) => { // In the case of the formatter using the multiline layout, we want to treat inline elements like we do words. // // ```html @@ -355,7 +414,7 @@ impl FormatHtmlElementList { if has_whitespace_between { Some(LineMode::SoftOrSpace) } else { - Some(LineMode::Soft) + None } } else { Some(LineMode::Hard) @@ -423,30 +482,6 @@ impl FormatHtmlElementList { } } - /// Tracks the tokens of [HtmlContent] nodes to be formatted and - /// asserts that the suppression comments are checked (they get ignored). - /// - /// This is necessary because the formatting of [HtmlContentList] bypasses the node formatting for - /// [HtmlContent] and instead, formats the nodes itself. - #[cfg(debug_assertions)] - fn disarm_debug_assertions(&self, node: &HtmlElementList, f: &mut HtmlFormatter) { - use biome_formatter::CstFormatContext; - - for child in node { - if let AnyHtmlElement::AnyHtmlContent(AnyHtmlContent::HtmlContent(text)) = child { - f.state_mut().track_token(&text.value_token().unwrap()); - - // You can't suppress a text node - f.context() - .comments() - .mark_suppression_checked(text.syntax()); - } - } - } - - #[cfg(not(debug_assertions))] - fn disarm_debug_assertions(&self, _: &HtmlElementList, _: &mut HtmlFormatter) {} - fn layout(&self, meta: ChildrenMeta) -> HtmlChildListLayout { match self.layout { HtmlChildListLayout::BestFitting => { diff --git a/crates/biome_html_formatter/src/lib.rs b/crates/biome_html_formatter/src/lib.rs index a2261235770c..c9aa8d2ec29f 100644 --- a/crates/biome_html_formatter/src/lib.rs +++ b/crates/biome_html_formatter/src/lib.rs @@ -1,7 +1,6 @@ #![deny(clippy::use_self)] -use crate::prelude::{format_bogus_node, format_suppressed_node}; -pub(crate) use crate::trivia::*; +use crate::prelude::*; use biome_formatter::comments::Comments; use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{CstFormatContext, FormatOwnedWithRule, FormatRefWithRule, prelude::*}; @@ -224,7 +223,7 @@ where /// You may want to override this method if you want to manually handle the formatting of comments /// inside of the `fmt_fields` method or customize the formatting of the leading comments. fn fmt_leading_comments(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { - format_leading_comments(node.syntax()).fmt(f) + format_html_leading_comments(node.syntax()).fmt(f) } /// Formats the [dangling comments](biome_formatter::comments#dangling-comments) of the node. @@ -245,7 +244,7 @@ where /// You may want to override this method if you want to manually handle the formatting of comments /// inside of the `fmt_fields` method or customize the formatting of the trailing comments. fn fmt_trailing_comments(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { - format_trailing_comments(node.syntax()).fmt(f) + format_html_trailing_comments(node.syntax()).fmt(f) } } diff --git a/crates/biome_html_formatter/src/prelude.rs b/crates/biome_html_formatter/src/prelude.rs index 769faa1d7d70..a4719f124984 100644 --- a/crates/biome_html_formatter/src/prelude.rs +++ b/crates/biome_html_formatter/src/prelude.rs @@ -5,7 +5,7 @@ pub(crate) use crate::{ AsFormat, FormatNodeRule, FormatResult, FormatRule, FormattedIterExt, HtmlFormatContext, - HtmlFormatter, format_removed, format_replaced, verbatim::*, + HtmlFormatter, trivia::*, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{AstNode, AstNodeList}; diff --git a/crates/biome_html_formatter/src/utils/children.rs b/crates/biome_html_formatter/src/utils/children.rs index 79c6b8c1608e..82f26cfd5a4e 100644 --- a/crates/biome_html_formatter/src/utils/children.rs +++ b/crates/biome_html_formatter/src/utils/children.rs @@ -7,9 +7,9 @@ use biome_formatter::{ Buffer, Format, FormatElement, FormatResult, format_args, prelude::*, write, }; use biome_html_syntax::{AnyHtmlContent, AnyHtmlElement}; -use biome_rowan::{SyntaxResult, TextLen, TextRange, TextSize, TokenText}; +use biome_rowan::{AstNode, SyntaxResult, TextLen, TextRange, TextSize, TokenText}; -use crate::{HtmlFormatter, comments::HtmlComments, context::HtmlFormatContext}; +use crate::{HtmlFormatter, context::HtmlFormatContext}; pub(crate) static HTML_WHITESPACE_CHARS: [u8; 4] = [b' ', b'\n', b'\t', b'\r']; @@ -75,6 +75,11 @@ pub(crate) enum HtmlChild { /// A Single word in a HTML text. For example, the words for `a b\nc` are `[a, b, c]` Word(HtmlWord), + /// A comment in a HTML text. + /// + /// This is considered a seperate kind of "word" here because we must preserve whitespace between text and comments. + Comment(HtmlWord), + /// A ` ` whitespace /// /// ```html @@ -116,6 +121,11 @@ pub(crate) enum HtmlChild { /// Any other content that isn't a text. Should be formatted as is. NonText(AnyHtmlElement), + + /// Any content that should be formatted verbatim, and not transformed in any way. + /// + /// Used for content that has formatting suppressed. + Verbatim(AnyHtmlElement), } impl HtmlChild { @@ -164,14 +174,14 @@ impl Format for HtmlRawSpace { pub(crate) fn html_split_children( children: I, - _comments: &HtmlComments, + f: &mut HtmlFormatter, ) -> SyntaxResult> where I: IntoIterator, { let mut builder = HtmlSplitChildrenBuilder::new(); - let mut prev_was_content = false; + let mut prev_child_was_content = false; for child in children { match child { AnyHtmlElement::AnyHtmlContent(AnyHtmlContent::HtmlContent(text)) => { @@ -179,6 +189,40 @@ where // Keep track if there's any leading/trailing empty line, new line or whitespace let value_token = text.value_token()?; + let is_suppressed = f.comments().is_suppressed(text.syntax()); + if is_suppressed { + builder.entry(HtmlChild::Verbatim(AnyHtmlElement::AnyHtmlContent( + text.into(), + ))); + continue; + } + f.state_mut().track_token(&value_token); + // Manually mark these comments as formatted because they are. Because we override the formatting of text content in here, the formatter does not seem to recognize them as formatted. + // We do have to manually check to make sure the comment's text range is actually inside this node's text range. Some comments may be included in this call to `leading_trailing_comments` that are not actually part of this node. + + let mut trailing_comments_to_format = vec![]; + for comment in f + .comments() + .leading_dangling_trailing_comments(text.syntax()) + { + let comment_range = comment.piece().text_range(); + // TODO: might be able to make this a debug assertion instead + if comment_range.start() >= value_token.text_range().start() + && comment_range.end() <= value_token.text_range().end() + { + comment.mark_formatted(); + } + } + for comment in f.comments().trailing_comments(text.syntax()) { + let comment_range = comment.piece().text_range(); + // TODO: might be able to make this a debug assertion instead + if !(comment_range.start() >= value_token.text_range().start() + && comment_range.end() <= value_token.text_range().end()) + { + trailing_comments_to_format.push(comment); + } + } + let mut chunks = HtmlSplitChunksIterator::new(value_token.text()).peekable(); // Text starting with a whitespace @@ -186,7 +230,7 @@ where // SAFETY: We just checked this above. match chunks.next().unwrap() { (_, HtmlTextChunk::Whitespace(whitespace)) => { - if whitespace.contains('\n') && !prev_was_content { + if whitespace.contains('\n') && !prev_child_was_content { if chunks.peek().is_none() { // A text only consisting of whitespace that also contains a new line isn't considered meaningful text. // It can be entirely removed from the content without changing the semantics. @@ -209,22 +253,58 @@ where builder.entry(HtmlChild::Newline) } else { - builder.entry(HtmlChild::Whitespace) + // if there's newlines before a comment, we need to preserve them + if whitespace.contains('\n') + && matches!( + chunks.peek(), + Some(&(_, HtmlTextChunk::Comment(_))) + ) + { + builder.entry(HtmlChild::Newline) + } else { + builder.entry(HtmlChild::Whitespace) + } } } _ => unreachable!(), } } + let mut prev_chunk_was_comment = false; while let Some(chunk) = chunks.next() { match chunk { (_, HtmlTextChunk::Whitespace(whitespace)) => { // Only handle trailing whitespace. Words must always be joined by new lines - if chunks.peek().is_none() { - if whitespace.contains('\n') { - builder.entry(HtmlChild::Newline); - } else { - builder.entry(HtmlChild::Whitespace) + let newlines = whitespace.chars().filter(|b| *b == '\n').count(); + match chunks.peek() { + Some(&(_, HtmlTextChunk::Comment(_))) => { + // if the next chunk is a comment, preserve the whitespace + if newlines >= 2 { + builder.entry(HtmlChild::EmptyLine) + } else if newlines == 1 { + builder.entry(HtmlChild::Newline) + } else { + builder.entry(HtmlChild::Whitespace) + } + } + None => { + if newlines >= 1 { + builder.entry(HtmlChild::Newline) + } else { + builder.entry(HtmlChild::Whitespace) + } + } + _ => { + // if the previous chunk was a comment, we need to preserve the whitespace before the next chunk. + if prev_chunk_was_comment { + if newlines >= 2 { + builder.entry(HtmlChild::EmptyLine) + } else if newlines == 1 { + builder.entry(HtmlChild::Newline) + } else { + builder.entry(HtmlChild::Whitespace) + } + } } } } @@ -237,9 +317,38 @@ where builder.entry(HtmlChild::Word(HtmlWord::new(text, source_position))); } + (relative_start, HtmlTextChunk::Comment(word)) => { + let text = value_token + .token_text() + .slice(TextRange::at(relative_start, word.text_len())); + let source_position = value_token.text_range().start() + relative_start; + + builder.entry(HtmlChild::Comment(HtmlWord::new(text, source_position))); + } + } + prev_chunk_was_comment = matches!(chunk, (_, HtmlTextChunk::Comment(_))); + } + + // There may be trailing comments that we attached to the content if this is the last child of an Element. They won't show up in the `value_token.text()` because they are actually attached to the leading token of the closing tag. This means we have to format them manually. + for comment in trailing_comments_to_format { + // This might not actually be the best way to handle the whitespace before the comment. If there are bugs here involving whitespace preceeding the comment, try this: + // Instead of the below match on `comment.lines_before()`, try to include the whitespace in the sliced range from the token text. Right now, that preceding whitespace is excluded, and we add it back in via the `lines_before` match below. + match comment.lines_before() { + 0 => {} + 1 => builder.entry(HtmlChild::Newline), + _ => builder.entry(HtmlChild::EmptyLine), } + let token = comment.piece().as_piece().token(); + let text = token.token_text(); + + builder.entry(HtmlChild::Comment(HtmlWord::new( + text.slice(comment.piece().text_range() - token.text_range().start()), + comment.piece().text_range().start(), + ))); + comment.mark_formatted(); } - prev_was_content = true; + + prev_child_was_content = true; } child => { let text = child.to_string(); @@ -275,8 +384,13 @@ where } } - builder.entry(HtmlChild::NonText(child)); - prev_was_content = false; + let is_suppressed = f.comments().is_suppressed(child.syntax()); + if is_suppressed { + builder.entry(HtmlChild::Verbatim(child)); + } else { + builder.entry(HtmlChild::NonText(child)); + } + prev_child_was_content = false; } } } @@ -304,7 +418,13 @@ impl HtmlSplitChildrenBuilder { Some(last @ (HtmlChild::EmptyLine | HtmlChild::Newline | HtmlChild::Whitespace)) => { if matches!(child, HtmlChild::Whitespace) { *last = child; - } else if matches!(child, HtmlChild::NonText(_) | HtmlChild::Word(_)) { + } else if matches!( + child, + HtmlChild::NonText(_) + | HtmlChild::Word(_) + | HtmlChild::Comment(_) + | HtmlChild::Verbatim(_) + ) { self.buffer.push(child); } } @@ -321,6 +441,7 @@ impl HtmlSplitChildrenBuilder { enum HtmlTextChunk<'a> { Whitespace(&'a str), Word(&'a str), + Comment(&'a str), } /// Splits a text into whitespace only and non-whitespace chunks. @@ -352,16 +473,54 @@ impl<'a> Iterator for HtmlSplitChunksIterator<'a> { self.position += char.text_len(); let is_whitespace = matches!(char, ' ' | '\n' | '\t' | '\r'); + let mut maybe_comment = char == '<'; + let mut definitely_comment = false; + let mut seen_end_comment_chars = 0; while let Some(next) = self.chars.peek() { - let next_is_whitespace = matches!(next, ' ' | '\n' | '\t' | '\r'); + if maybe_comment && !definitely_comment { + match (self.position - start, next) { + (idx, '!') if idx == 1.into() => {} + (idx, '-') if idx == 2.into() || idx == 3.into() => {} + (idx, _) if idx == 4.into() => { + definitely_comment = true; + } + _ => { + maybe_comment = false; + } + } + } - if is_whitespace != next_is_whitespace { - break; + if definitely_comment { + match (seen_end_comment_chars, next) { + (0, '-') => seen_end_comment_chars += 1, + (1, '-') => seen_end_comment_chars += 1, + (2, '>') => seen_end_comment_chars += 1, + _ => seen_end_comment_chars = 0, + } + } else { + let next_is_whitespace = matches!(next, ' ' | '\n' | '\t' | '\r'); + + if is_whitespace != next_is_whitespace { + break; + } } self.position += next.text_len(); self.chars.next(); + + if seen_end_comment_chars == 3 { + break; + } + // we also need to stop progressing if we encounter a comment start + let peek_end = self.position + TextSize::from(4); + if !definitely_comment + && peek_end <= TextSize::from(self.text.len() as u32) + && self.text.is_char_boundary(peek_end.into()) + && &self.text[TextRange::new(self.position, peek_end)] == " + // foo bar baz boof + // quick brown fox + // "#; + let src = r#" +foo bar baz boof + +quick brown fox +"#; let source_type = HtmlFileSource::html(); let tree = parse_html(src, source_type); let options = HtmlFormatOptions::new(HtmlFileSource::html()) .with_indent_style(IndentStyle::Space) .with_line_width(LineWidth::try_from(80).unwrap()) - .with_attribute_position(AttributePosition::Multiline); + .with_attribute_position(AttributePosition::Auto); let doc = format_node(options.clone(), &tree.syntax()).unwrap(); let result = doc.print().unwrap(); diff --git a/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html b/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html new file mode 100644 index 000000000000..1414f859fb8d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html @@ -0,0 +1,5 @@ +
+ Foo + + +
diff --git a/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html.snap new file mode 100644 index 000000000000..98c36d66d7c7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/after-text-inside-div.html.snap @@ -0,0 +1,41 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/after-text-inside-div.html +--- +# Input + +```html +
+ Foo + + +
+ +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +
+ Foo + + +
+``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/after-text.html b/crates/biome_html_formatter/tests/specs/html/comments/after-text.html new file mode 100644 index 000000000000..4ccc9ae7ba84 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/after-text.html @@ -0,0 +1,3 @@ +Foo + + diff --git a/crates/biome_html_formatter/tests/specs/html/comments/after-text.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/after-text.html.snap new file mode 100644 index 000000000000..ba7656ba7638 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/after-text.html.snap @@ -0,0 +1,36 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/after-text.html +--- +# Input + +```html +Foo + + + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +Foo + +``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html b/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html new file mode 100644 index 000000000000..a1117e4a41a5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html @@ -0,0 +1,2 @@ + +bar diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html.snap new file mode 100644 index 000000000000..efbb11985ed1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text-2.html.snap @@ -0,0 +1,35 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/before-text-2.html +--- +# Input + +```html + +bar + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + +bar +``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html b/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html new file mode 100644 index 000000000000..c2e1a59cf959 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html @@ -0,0 +1,5 @@ +
+ + + Bar +
diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html.snap new file mode 100644 index 000000000000..d013d7d6c89f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text-inside-div.html.snap @@ -0,0 +1,41 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/before-text-inside-div.html +--- +# Input + +```html +
+ + + Bar +
+ +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +
+ + + Bar +
+``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text.html b/crates/biome_html_formatter/tests/specs/html/comments/before-text.html new file mode 100644 index 000000000000..2181a943bd9b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text.html @@ -0,0 +1,3 @@ + + +Bar diff --git a/crates/biome_html_formatter/tests/specs/html/comments/before-text.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/before-text.html.snap new file mode 100644 index 000000000000..84227ca3cc24 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/before-text.html.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/before-text.html +--- +# Input + +```html + + +Bar + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + + +Bar +``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html b/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html new file mode 100644 index 000000000000..370a6d2d1434 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html @@ -0,0 +1,3 @@ +
+
+
\ No newline at end of file diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html.snap new file mode 100644 index 000000000000..f3dce4223c82 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inline-with-block-element.html.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/inline-with-block-element.html +--- +# Input + +```html +
+
+
+``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + +
+
+
+``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inline.html b/crates/biome_html_formatter/tests/specs/html/comments/inline.html new file mode 100644 index 000000000000..ce94aebba9fa --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inline.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inline.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/inline.html.snap new file mode 100644 index 000000000000..dfaf0e3efce6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inline.html.snap @@ -0,0 +1,33 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/inline.html +--- +# Input + +```html + + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + +``` diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html b/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html new file mode 100644 index 000000000000..81aa72439e3b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html @@ -0,0 +1,3 @@ +foo bar baz floof + +quick brown fox diff --git a/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html.snap b/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html.snap new file mode 100644 index 000000000000..8d22da052b6e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/comments/inside-text.html.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: comments/inside-text.html +--- +# Input + +```html +foo bar baz floof + +quick brown fox + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +foo bar baz floof + +quick brown fox +``` diff --git a/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html b/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html index ffcd1131e543..1bb4ae0be671 100644 --- a/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html +++ b/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html @@ -1,5 +1,5 @@ - + diff --git a/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html.snap b/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html.snap index fa4530230fb1..62db18787a6e 100644 --- a/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html.snap +++ b/crates/biome_html_formatter/tests/specs/html/elements/spacing/case3.html.snap @@ -7,7 +7,7 @@ snapshot_kind: text ```html - + @@ -36,13 +36,7 @@ Self close void elements: never ```html - + + test ``` - - - -## Unimplemented nodes/tokens - -"" => 0..12 -"\n" => 13..26 diff --git a/crates/biome_html_formatter/tests/specs/html/long-inline-elements.html.snap b/crates/biome_html_formatter/tests/specs/html/long-inline-elements.html.snap index dd2a157400e4..6a89a17652d5 100644 --- a/crates/biome_html_formatter/tests/specs/html/long-inline-elements.html.snap +++ b/crates/biome_html_formatter/tests/specs/html/long-inline-elements.html.snap @@ -1,7 +1,6 @@ --- source: crates/biome_formatter_test/src/snapshot_builder.rs info: long-inline-elements.html -snapshot_kind: text --- # Input @@ -48,8 +47,7 @@ Lorem ipsumdolor sit amet, consectetur adipiscing elit. Sed eu diam vel sem congue pulvinar at vitae purus. Morbi volutpat arcu massa, eu laoreet eros feugiat ac. -Etiam sit amet turpis blandit, volutpat magna nec, luctus justo. Nam nec diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html new file mode 100644 index 000000000000..1a7f621190be --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html @@ -0,0 +1,5 @@ + + +

+ Test Test Test +

diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html.snap b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html.snap new file mode 100644 index 000000000000..c23873ef1e33 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression-2.html.snap @@ -0,0 +1,40 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: suppressions/basic-suppression-2.html +--- +# Input + +```html + + +

+ Test Test Test +

+ +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + +

+ Test Test Test +

+``` diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html new file mode 100644 index 000000000000..b9c2f0b49f8a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html @@ -0,0 +1,2 @@ + +
This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior.
\ No newline at end of file diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html.snap b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html.snap new file mode 100644 index 000000000000..6beb599f5c76 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/basic-suppression.html.snap @@ -0,0 +1,39 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: suppressions/basic-suppression.html +--- +# Input + +```html + +
This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior.
+``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html + +
This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior.
+``` + +# Lines exceeding max width of 80 characters +``` + 2:
This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior.
+``` diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html new file mode 100644 index 000000000000..a2a327553d71 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html @@ -0,0 +1 @@ +1234 56 diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html.snap b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html.snap new file mode 100644 index 000000000000..879f185dabf1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-content.html.snap @@ -0,0 +1,33 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: suppressions/inline-content.html +--- +# Input + +```html +1234 56 + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +1234 56 +``` diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html new file mode 100644 index 000000000000..ce5ff273d824 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html @@ -0,0 +1 @@ +12 3 \ No newline at end of file diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html.snap b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html.snap new file mode 100644 index 000000000000..8180dfb95ec1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/inline-elements.html.snap @@ -0,0 +1,32 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: suppressions/inline-elements.html +--- +# Input + +```html +12 3 +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +12 3 +``` diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html b/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html new file mode 100644 index 000000000000..77c8589eb3f4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html @@ -0,0 +1,4 @@ +
+ + This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior. +
diff --git a/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html.snap b/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html.snap new file mode 100644 index 000000000000..29dcf9e2f0b7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/suppressions/suppress-inside-element.html.snap @@ -0,0 +1,44 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: suppressions/suppress-inside-element.html +--- +# Input + +```html +
+ + This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior. +
+ +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: css +Indent script and style: false +Self close void elements: never +----- + +```html +
+ + This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior. +
+``` + +# Lines exceeding max width of 80 characters +``` + 3: This is some really long content that should be broken on multiple lines. However, it won't because there is a suppression comment. This is the desired behavior. +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/comment.html.snap similarity index 52% rename from crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap rename to crates/biome_html_formatter/tests/specs/prettier/html/basics/comment.html.snap index 5147537c5209..dff88a3b2bcf 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/comment.html.snap @@ -1,13 +1,11 @@ --- source: crates/biome_formatter_test/src/snapshot_builder.rs -info: html/comments/before-text.html +info: html/basics/comment.html --- # Input ```html - - -123 + ``` @@ -17,15 +15,13 @@ info: html/comments/before-text.html ```diff --- Prettier +++ Biome -@@ -1,3 +1,2 @@ - -- - 123 +@@ -1 +1 @@ +- ++ +\ No newline at end of file ``` # Output ```html - -123 -``` +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap index 44d93b300530..df5b02c0ed05 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap @@ -29,7 +29,7 @@ info: html/basics/void-elements-2.html ```diff --- Prettier +++ Biome -@@ -1,12 +1,17 @@ +@@ -1,12 +1,16 @@ 1 + -+ -+1 ++1 -1 + - -1 +1 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap index 7e9c2dc4499b..56b7c7d1791d 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap @@ -27,25 +27,21 @@ text ```diff --- Prettier +++ Biome -@@ -12,11 +12,14 @@ +@@ -12,11 +12,11 @@ > text -texttext -+text text -texttexttexttexttext -+text -+text -+text -+text -+text ++texttexttexttexttext ``` # Output @@ -65,17 +61,14 @@ text > text -text -text text -text -text -text -text -text +texttexttexttexttext ``` # Lines exceeding max width of 80 characters @@ -83,5 +76,5 @@ text 2: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" 7: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" 11: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" - 17: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + 16: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" ``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.snap new file mode 100644 index 000000000000..e1a4c07425e4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.snap @@ -0,0 +1,78 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/bogus.html +--- +# Input + +```html + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,2 +1,2 @@ +- ++< ? hello ?> + +``` + +# Output + +```html +< ? hello ?> + +``` + +# Errors +``` +bogus.html:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unescaped `<` bracket character. Expected a tag or escaped character. + + > 1 │ + │ ^ + 2 │ + 3 │ + + i Replace this character with `<` to escape it. + +bogus.html:2:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 1 │ + > 2 │ + │ ^ + 3 │ + + i Expected an element name here. + + 1 │ + > 2 │ + │ ^ + 3 │ + +bogus.html:3:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `>` but instead the file ends + + 1 │ + 2 │ + > 3 │ + │ + + i the file ends here + + 1 │ + 2 │ + > 3 │ + │ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap index 804e3197bbc8..a94fcc828236 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap @@ -271,6 +271,26 @@ conditional.html:14:2 parse ━━━━━━━━━━━━━━━━━ 15 │ 16 │ +conditional.html:16:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 14 │ + 15 │ + > 16 │ + │ ^ + 17 │ + 18 │ + + i Expected an attribute here. + + 14 │ + 15 │ + > 16 │ + │ ^ + 17 │ + 18 │ + conditional.html:21:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -291,6 +311,26 @@ conditional.html:21:2 parse ━━━━━━━━━━━━━━━━━ 22 │ 23 │ +conditional.html:23:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 21 │ + 22 │ + > 23 │ + │ ^ + 24 │ + 25 │ + + i Expected an attribute here. + + 21 │ + 22 │ + > 23 │ + │ ^ + 24 │ + 25 │ + conditional.html:28:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -311,6 +351,26 @@ conditional.html:28:2 parse ━━━━━━━━━━━━━━━━━ 29 │ 30 │ +conditional.html:30:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 28 │ + 29 │ + > 30 │ + │ ^ + 31 │ + 32 │ + + i Expected an attribute here. + + 28 │ + 29 │ + > 30 │ + │ ^ + 31 │ + 32 │ + conditional.html:43:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -331,6 +391,26 @@ conditional.html:43:2 parse ━━━━━━━━━━━━━━━━━ 44 │ 45 │ +conditional.html:45:24 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 43 │ + 44 │ + > 45 │ + │ ^ + 46 │ + 47 │ + + i Expected an attribute here. + + 43 │ + 44 │ + > 45 │ + │ ^ + 46 │ + 47 │ + conditional.html:50:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -351,6 +431,26 @@ conditional.html:50:2 parse ━━━━━━━━━━━━━━━━━ 51 │ 52 │ +conditional.html:52:24 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 50 │ + 51 │ + > 52 │ + │ ^ + 53 │ + 54 │ + + i Expected an attribute here. + + 50 │ + 51 │ + > 52 │ + │ ^ + 53 │ + 54 │ + conditional.html:57:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -371,6 +471,26 @@ conditional.html:57:2 parse ━━━━━━━━━━━━━━━━━ 58 │ 59 │ +conditional.html:59:24 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 57 │ + 58 │ + > 59 │ + │ ^ + 60 │ + 61 │ + + i Expected an attribute here. + + 57 │ + 58 │ + > 59 │ + │ ^ + 60 │ + 61 │ + conditional.html:64:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an element name but instead found '!'. @@ -391,6 +511,26 @@ conditional.html:64:2 parse ━━━━━━━━━━━━━━━━━ 65 │ 66 │ + 66 │ + │ ^ + 68 │ + 69 │ + + i Expected an attribute here. + + 65 │ + 66 │ + │ ^ + 68 │ + 69 │ + conditional.html:71:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a closing tag but instead found the end of the file. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap index a281bf3500a5..ecb1dfe2972b 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap @@ -32,12 +32,22 @@ info: html/comments/for_debugging.html ```diff --- Prettier +++ Biome -@@ -1,4 +1,4 @@ +@@ -1,14 +1,12 @@ - + +- + +- + ``` # Output @@ -49,11 +59,9 @@ info: html/comments/for_debugging.html - - diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.ignored similarity index 100% rename from crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html rename to crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.ignored diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap index c397efa8bc57..e9477afb9082 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap @@ -50,5 +50,20 @@ empty2.html:1:1 parse ━━━━━━━━━━━━━━━━━━━ i Remove it or rename the file to have the .astro extension. +empty2.html:5:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 4 │
+ > 5 │ --- + │ ^^^ + 6 │
+ 7 │ + + i Expected one of: + + - element + - text + ``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap deleted file mode 100644 index ec54648387fd..000000000000 --- a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap +++ /dev/null @@ -1,77 +0,0 @@ ---- -source: crates/biome_formatter_test/src/snapshot_builder.rs -info: html/pragma/no-pragma.html ---- -# Input - -```html - - - - - - - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Biome -@@ -1,3 +1,4 @@ - - -- -+ -+ -``` - -# Output - -```html - - - - -``` - -# Errors -``` -no-pragma.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected an element name but instead found '!'. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - - i Expected an element name here. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - -no-pragma.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a closing tag but instead found the end of the file. - - 5 │ - 6 │ - > 7 │ - │ - - i Expected a closing tag here. - - 5 │ - 6 │ - > 7 │ - │ - - -``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap deleted file mode 100644 index df6335f4be27..000000000000 --- a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap +++ /dev/null @@ -1,77 +0,0 @@ ---- -source: crates/biome_formatter_test/src/snapshot_builder.rs -info: html/pragma/with-pragma-2.html ---- -# Input - -```html - - - - - - - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Biome -@@ -1,3 +1,4 @@ - - -- -+ -+ -``` - -# Output - -```html - - - - -``` - -# Errors -``` -with-pragma-2.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected an element name but instead found '!'. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - - i Expected an element name here. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - -with-pragma-2.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a closing tag but instead found the end of the file. - - 5 │ - 6 │ - > 7 │ - │ - - i Expected a closing tag here. - - 5 │ - 6 │ - > 7 │ - │ - - -``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap deleted file mode 100644 index e4cf04ca2cbd..000000000000 --- a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap +++ /dev/null @@ -1,77 +0,0 @@ ---- -source: crates/biome_formatter_test/src/snapshot_builder.rs -info: html/pragma/with-pragma.html ---- -# Input - -```html - - - - - - - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Biome -@@ -1,3 +1,4 @@ - - -- -+ -+ -``` - -# Output - -```html - - - - -``` - -# Errors -``` -with-pragma.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected an element name but instead found '!'. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - - i Expected an element name here. - - 3 │ - > 4 │ - │ ^ - 5 │ - 6 │ - -with-pragma.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a closing tag but instead found the end of the file. - - 5 │ - 6 │ - > 7 │ - │ - - i Expected a closing tag here. - - 5 │ - 6 │ - > 7 │ - │ - - -``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap index 9f862c301eb0..ee816e184330 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap @@ -17,14 +17,12 @@ info: html/prettier_ignore/cases.html ```diff --- Prettier +++ Biome -@@ -1,4 +1,5 @@ +@@ -1,4 +1,3 @@ 123456 - -+ -+ -+ ++ ``` # Output @@ -32,7 +30,5 @@ info: html/prettier_ignore/cases.html ```html 123456 - - - + ``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap deleted file mode 100644 index c3643cb13f2f..000000000000 --- a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap +++ /dev/null @@ -1,167 +0,0 @@ ---- -source: crates/biome_formatter_test/src/snapshot_builder.rs -info: html/prettier_ignore/document.html ---- -# Input - -```html - - - - - - Title - - - -

- Test Test Test -

- - -

- Test Test Test -

- - -
  • First
  • Second1
    String
- - -
- - - - - -
- - -
  • First
  • Second
- - -
- - - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Biome -@@ -7,35 +7,36 @@ - - - --

-- Test Test Test --

-+

Test Test Test

- - --

-- Test Test Test --

-+

Test Test Test

- - --
  • First
  • Second1
    String
-+
    -+
  • First
  • -+
  • -+ Second1 -+
    -+
    String
    -+
    -+
  • -+
- - --
-- -- -- -- -- --
-+
- - --
  • First
  • Second
-+
    -+ -+
  • First
  • -+ -+
  • Second
  • -+ -+
- - -
-``` - -# Output - -```html - - - - - - Title - - - -

Test Test Test

- - -

Test Test Test

- - -
    -
  • First
  • -
  • - Second1 -
    -
    String
    -
    -
  • -
- - -
- - -
    - -
  • First
  • - -
  • Second
  • - -
- - -
- - -``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap index 2f96e1c210c4..d95ce473bb11 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap @@ -29,39 +29,21 @@ A super long string that has been marked as ignore because it was probably gener ```diff --- Prettier +++ Biome -@@ -1,5 +1,5 @@ -- --A super long string that has been marked as ignore because it was probably generated by some script. -+A super long string that has been -+marked as ignore because it was probably generated by some script. - -

- Just some ordinary text that should be wrapped up because it is super long and -@@ -7,11 +7,11 @@ +@@ -10,7 +10,6 @@ + + A super long string that has been marked as ignore because it was probably generated by some script.

- -

-- -- A super long string that has been marked as ignore because it was probably generated by some script. -+ A super long string that has been -+ marked as ignore because it was probably generated by some script. -

- -- --| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | --| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | --| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | -+| Dogs | Cats | Weasels | Bats | -+Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | | ---- | ---- | ------- | -+---- | ---- | ---- | --------- | --------- | ---- | ------ | | 1 | 1 | 0 | 0 | 1 -+| 1 | 5 | 16 | 4 | 0 | +- + + | Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | + | ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | ``` # Output ```html -A super long string that has been -marked as ignore because it was probably generated by some script. + +A super long string that has been marked as ignore because it was probably generated by some script.

Just some ordinary text that should be wrapped up because it is super long and @@ -69,12 +51,20 @@ marked as ignore because it was probably generated by some script.

- A super long string that has been - marked as ignore because it was probably generated by some script. + + A super long string that has been marked as ignore because it was probably generated by some script.

+ +| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | +| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | +| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | +``` -| Dogs | Cats | Weasels | Bats | -Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | | ---- | ---- | ------- | ----- | ---- | ---- | --------- | --------- | ---- | ------ | | 1 | 1 | 0 | 0 | 1 -| 1 | 5 | 16 | 4 | 0 | +# Lines exceeding max width of 80 characters +``` + 2: A super long string that has been marked as ignore because it was probably generated by some script. + 11: A super long string that has been marked as ignore because it was probably generated by some script. + 14: | Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | + 15: | ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | + 16: | 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | ``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap index 06b7ec0495b2..e08a6d50c90c 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap @@ -23,10 +23,12 @@ info: html/text/tag-should-in-fill.html - >foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo - bar +- + + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + - ++ +\ No newline at end of file ``` # Output @@ -35,5 +37,4 @@ info: html/text/tag-should-in-fill.html foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar - -``` +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap index bb1dbc5c6cf6..caf20df3dfbe 100644 --- a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap @@ -24,7 +24,7 @@ info: html/whitespace/nested-inline-without-whitespace.html ```diff --- Prettier +++ Biome -@@ -1,22 +1,23 @@ +@@ -1,22 +1,26 @@ /ˌəˈnɔɪ/ˌ -+ ɪ -+ l -+ ə -+ ˈ -+ n -+ ɔɪˌɪləˈnɔɪ/ @@ -49,17 +51,15 @@ info: html/whitespace/nested-inline-without-whitespace.html >ipipsumi -+ p -+ s -+ u -+ msum ``` @@ -69,25 +69,28 @@ info: html/whitespace/nested-inline-without-whitespace.html ```html /ˌ - ɪ - l - ə - ˈ - n - ɔɪˌɪləˈnɔɪ/ i - p - s - u - mipsum ``` diff --git a/crates/biome_html_parser/src/lexer/mod.rs b/crates/biome_html_parser/src/lexer/mod.rs index 2b45b684266e..47189c36a844 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::{HtmlEmbeddedLanguage, HtmlLexContext}; use biome_html_syntax::HtmlSyntaxKind::{ - DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL, NEWLINE, TOMBSTONE, - UNICODE_BOM, WHITESPACE, + COMMENT, DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL, NEWLINE, + TOMBSTONE, UNICODE_BOM, WHITESPACE, }; use biome_html_syntax::{HtmlSyntaxKind, T, TextLen, TextSize}; use biome_parser::diagnostic::ParseDiagnostic; @@ -50,8 +50,8 @@ impl<'src> HtmlLexer<'src> { } } - /// Consume a token in the [HtmlLexContext::Regular] context. - fn consume_token(&mut self, current: u8) -> HtmlSyntaxKind { + /// Consume a token in the [HtmlLexContext::InsideTag] context. + fn consume_token_inside_tag(&mut self, current: u8) -> HtmlSyntaxKind { match current { b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(), b'<' => self.consume_l_angle(), @@ -59,8 +59,11 @@ impl<'src> HtmlLexer<'src> { b'/' => self.consume_byte(T![/]), b'=' => self.consume_byte(T![=]), b'!' => self.consume_byte(T![!]), + b'{' if self.is_at_l_text_expression() => self.consume_l_text_expression(), + b'{' => self.consume_byte(T!['{']), + b'}' if self.is_at_r_text_expression() => self.consume_r_text_expression(), + b'}' => self.consume_byte(T!['}']), b'\'' | b'"' => self.consume_string_literal(current), - b'-' if self.is_at_frontmatter_edge() => self.consume_frontmatter_edge(), _ if self.current_kind == T![<] && is_tag_name_byte(current) => { // tag names must immediately follow a `<` // https://html.spec.whatwg.org/multipage/syntax.html#start-tags @@ -81,18 +84,23 @@ impl<'src> HtmlLexer<'src> { } } - /// Consume a token in the [HtmlLexContext::OutsideTag] context. - fn consume_token_outside_tag(&mut self, current: u8) -> HtmlSyntaxKind { + /// Consume a token in the [HtmlLexContext::Regular] context. + fn consume_token(&mut self, current: u8) -> HtmlSyntaxKind { match current { b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(), b'{' if self.is_at_l_text_expression() => self.consume_l_text_expression(), + b'{' => self.consume_byte(T!['{']), b'}' if self.is_at_r_text_expression() => self.consume_r_text_expression(), + b'}' => self.consume_byte(T!['}']), + b'!' if self.current() == T![<] => self.consume_byte(T![!]), + b'/' if self.current() == T![<] => self.consume_byte(T![/]), + b'-' if self.is_at_frontmatter_edge() => self.consume_frontmatter_edge(), b'<' => { // if this truly is the start of a tag, it *must* be immediately followed by a tag name. Whitespace is not allowed. // https://html.spec.whatwg.org/multipage/syntax.html#start-tags if self .peek_byte() - .is_some_and(|b| is_tag_name_byte(b) || b == b'!' || b == b'/') + .is_some_and(|b| is_tag_name_byte(b) || b == b'!' || b == b'/' || b == b'>') { self.consume_l_angle() } else { @@ -171,22 +179,20 @@ 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 + fn consume_comment(&mut self) -> HtmlSyntaxKind { + // eat + self.advance(3); + return COMMENT; } + self.advance_byte_or_char(char); } + + COMMENT } /// Consume a token in the [HtmlLexContext::CdataSection] context. @@ -287,20 +293,10 @@ impl<'src> HtmlLexer<'src> { fn consume_tag_name(&mut self, first: u8) -> HtmlSyntaxKind { self.assert_current_char_boundary(); - const BUFFER_SIZE: usize = 14; - let mut buffer = [0u8; BUFFER_SIZE]; - buffer[0] = first; - let mut len = 1; - self.advance_byte_or_char(first); while let Some(byte) = self.current_byte() { if is_tag_name_byte(byte) { - if len < BUFFER_SIZE { - buffer[len] = byte; - len += 1; - } - self.advance(1) } else { break; @@ -425,7 +421,7 @@ impl<'src> HtmlLexer<'src> { self.assert_byte(b'<'); if self.at_start_comment() { - self.consume_comment_start() + self.consume_comment() } else if self.at_start_cdata() { self.consume_cdata_start() } else { @@ -496,20 +492,6 @@ impl<'src> HtmlLexer<'src> { self.current_byte() == Some(b'}') && self.byte_at(1) == Some(b'}') } - fn consume_comment_start(&mut self) -> HtmlSyntaxKind { - debug_assert!(self.at_start_comment()); - - self.advance(4); - T![] - } - fn consume_cdata_start(&mut self) -> HtmlSyntaxKind { debug_assert!(self.at_start_cdata()); @@ -572,16 +554,27 @@ impl<'src> HtmlLexer<'src> { Ok(()) } - /// Consume HTML text literals outside of tags. + /// Consume a single block of HTML text outside of tags. /// - /// This includes text and single spaces between words. If newline or a second - /// consecutive space is found, this will stop consuming and to allow the lexer to - /// switch to `consume_whitespace`. + /// We consider a "block" of text to be a sequence of words, with whitespace + /// separating them. A block ends when there is 2 newlines, or when a special + /// character (eg. `<`) is found. /// - /// See: https://html.spec.whatwg.org/#space-separated-tokens - /// See: https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace + /// Spaces between words are treated the same as newlines between words in HTML, + /// and we don't end a block when we encounter a newline. However, we do not + /// include leading or trailing whitespace in the block, letting the lexer + /// consume that whitespace as trivia. + /// + /// This makes it easier for users to suppress formatting for specific blocks + /// of text instead of needing to suppress the entire parent element, which may + /// not even be present if the text is at the root level. + /// + /// - See: + /// - See: fn consume_html_text(&mut self) -> HtmlSyntaxKind { let mut whitespace_started = None; + let mut seen_newlines = 0; + let mut closing_expression = None; let mut was_escaped = false; @@ -611,36 +604,42 @@ impl<'src> HtmlLexer<'src> { } b'<' => { - if let Some(checkpoint) = whitespace_started { - // avoid treating the last space as part of the token if there is one - self.rewind(checkpoint); - } break; } b'\n' | b'\r' => { + if whitespace_started.is_none() { + whitespace_started = Some(self.checkpoint()); + } self.after_newline = true; - break; + seen_newlines += 1; + if seen_newlines > 1 { + break; + } + self.advance(1); } - b' ' => { + b' ' | b'\t' => { if was_escaped { was_escaped = false; } - if let Some(checkpoint) = whitespace_started { - // avoid treating the last space as part of the token - self.rewind(checkpoint); - break; + if whitespace_started.is_none() { + whitespace_started = Some(self.checkpoint()); } - whitespace_started = Some(self.checkpoint()); closing_expression = None; self.advance(1); } _ => { self.advance(1); whitespace_started = None; + seen_newlines = 0; } } } + if let Some(checkpoint) = whitespace_started { + // avoid treating the trailing whitespace as part of the token if there is any + self.rewind(checkpoint); + } + HTML_LITERAL } } @@ -674,13 +673,12 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> { match self.current_byte() { Some(current) => match context { HtmlLexContext::Regular => self.consume_token(current), - HtmlLexContext::OutsideTag => self.consume_token_outside_tag(current), + HtmlLexContext::InsideTag => self.consume_token_inside_tag(current), HtmlLexContext::AttributeValue => self.consume_token_attribute_value(current), HtmlLexContext::Doctype => self.consume_token_doctype(current), HtmlLexContext::EmbeddedLanguage(lang) => { self.consume_token_embedded_language(current, lang, context) } - HtmlLexContext::Comment => self.consume_inside_comment(current), HtmlLexContext::CdataSection => self.consume_inside_cdata(current), HtmlLexContext::AstroFencedCodeBlock => { self.consume_astro_frontmatter(current, context) diff --git a/crates/biome_html_parser/src/lexer/tests.rs b/crates/biome_html_parser/src/lexer/tests.rs index dbf8411069f4..5e8ca0cfc98d 100644 --- a/crates/biome_html_parser/src/lexer/tests.rs +++ b/crates/biome_html_parser/src/lexer/tests.rs @@ -122,6 +122,7 @@ fn doctype_upper_key() { #[test] fn string_literal() { assert_lex! { + HtmlLexContext::InsideTag, "\"data-attribute\"", HTML_STRING_LITERAL: 16, } @@ -130,6 +131,7 @@ fn string_literal() { #[test] fn self_closing() { assert_lex! { + HtmlLexContext::InsideTag, "
", L_ANGLE: 1, HTML_LITERAL: 3, @@ -142,6 +144,7 @@ fn self_closing() { #[test] fn element() { assert_lex! { + HtmlLexContext::InsideTag, "
", L_ANGLE: 1, HTML_LITERAL: 3, @@ -156,7 +159,6 @@ fn element() { #[test] fn html_text() { assert_lex! { - HtmlLexContext::OutsideTag, "abcdefghijklmnopqrstuvwxyz!@_-:;", HTML_LITERAL: 32, } @@ -195,6 +197,7 @@ fn doctype_with_quirk_and_system() { #[test] fn element_with_attributes() { assert_lex! { + HtmlLexContext::InsideTag, "
", L_ANGLE: 1, HTML_LITERAL: 3, @@ -209,6 +212,7 @@ fn element_with_attributes() { #[test] fn element_with_dashed_attributes() { assert_lex! { + HtmlLexContext::InsideTag, "
", L_ANGLE: 1, HTML_LITERAL: 3, @@ -221,6 +225,7 @@ fn element_with_dashed_attributes() { #[test] fn html_element() { assert_lex! { + HtmlLexContext::InsideTag, "", L_ANGLE: 1, HTML_LITERAL: 4, @@ -235,7 +240,6 @@ fn html_element() { #[test] fn html_text_spaces() { assert_lex! { - HtmlLexContext::OutsideTag, "Lorem ipsum dolor sit amet, consectetur.", HTML_LITERAL: 40, } @@ -244,20 +248,15 @@ fn html_text_spaces() { #[test] fn html_text_spaces_with_lines() { assert_lex! { - HtmlLexContext::OutsideTag, "Lorem ipsum dolor sit amet, consectetur.", - HTML_LITERAL: 21, - NEWLINE: 1, - WHITESPACE: 8, - HTML_LITERAL: 18, + HTML_LITERAL: 48, } } #[test] fn long_text() { assert_lex! { - HtmlLexContext::OutsideTag, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed dapibus velit non justo", HTML_LITERAL: 84, } @@ -266,7 +265,7 @@ fn long_text() { #[test] fn text_trailing_whitespace() { assert_lex! { - HtmlLexContext::OutsideTag, + HtmlLexContext::Regular, "Lorem ipsum dolor ", - COMMENT_END: 3, - } -} - -#[test] -fn comment_full() { - assert_lex! { - HtmlLexContext::Comment, - "", - COMMENT_START: 4, - HTML_LITERAL: 5, - COMMENT_END: 3, - } -} - #[test] fn cdata_full() { assert_lex! { diff --git a/crates/biome_html_parser/src/syntax/mod.rs b/crates/biome_html_parser/src/syntax/mod.rs index 8bee033b7393..ca828b5530e3 100644 --- a/crates/biome_html_parser/src/syntax/mod.rs +++ b/crates/biome_html_parser/src/syntax/mod.rs @@ -76,8 +76,8 @@ fn parse_doc_type(p: &mut HtmlParser) -> ParsedSyntax { } let m = p.start(); - p.bump(T![<]); - p.bump(T![!]); + p.bump_with_context(T![<], HtmlLexContext::InsideTag); + p.bump_with_context(T![!], HtmlLexContext::Doctype); if p.at(T![doctype]) { p.eat_with_context(T![doctype], HtmlLexContext::Doctype); @@ -110,7 +110,7 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { } let m = p.start(); - p.bump(T![<]); + p.bump_with_context(T![<], HtmlLexContext::InsideTag); let opening_tag_name = p.cur_text().to_string(); let should_be_self_closing = VOID_ELEMENTS .iter() @@ -123,15 +123,15 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { AttributeList.parse_list(p); if p.at(T![/]) { - p.bump(T![/]); - p.expect_with_context(T![>], HtmlLexContext::OutsideTag); + p.bump_with_context(T![/], HtmlLexContext::InsideTag); + p.expect_with_context(T![>], HtmlLexContext::Regular); Present(m.complete(p, HTML_SELF_CLOSING_ELEMENT)) } else { if should_be_self_closing { if p.at(T![/]) { - p.bump(T![/]); + p.bump_with_context(T![/], HtmlLexContext::InsideTag); } - p.expect_with_context(T![>], HtmlLexContext::OutsideTag); + p.expect_with_context(T![>], HtmlLexContext::Regular); return Present(m.complete(p, HTML_SELF_CLOSING_ELEMENT)); } p.expect_with_context( @@ -144,7 +144,7 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { _ => unreachable!(), }) } else { - HtmlLexContext::OutsideTag + HtmlLexContext::Regular }, ); let opening = m.complete(p, HTML_OPENING_ELEMENT); @@ -172,8 +172,8 @@ fn parse_closing_tag(p: &mut HtmlParser) -> ParsedSyntax { return Absent; } let m = p.start(); - p.bump(T![<]); - p.bump(T![/]); + p.bump_with_context(T![<], HtmlLexContext::InsideTag); + p.bump_with_context(T![/], HtmlLexContext::InsideTag); let should_be_self_closing = VOID_ELEMENTS .iter() .any(|tag| tag.eq_ignore_ascii_case(p.cur_text())); @@ -183,11 +183,11 @@ fn parse_closing_tag(p: &mut HtmlParser) -> ParsedSyntax { let _name = parse_literal(p, HTML_TAG_NAME); // There shouldn't be any attributes in a closing tag. - while p.at(HTML_LITERAL) { + while p.at(HTML_LITERAL) || p.at(T!["{{"]) || p.at(T!["}}"]) { p.error(closing_tag_should_not_have_attributes(p, p.cur_range())); - p.bump_remap(HTML_BOGUS); + p.bump_remap_with_context(HTML_BOGUS, HtmlLexContext::InsideTag); } - p.bump_with_context(T![>], HtmlLexContext::OutsideTag); + p.bump_with_context(T![>], HtmlLexContext::Regular); Present(m.complete(p, HTML_CLOSING_ELEMENT)) } @@ -201,12 +201,11 @@ impl ParseNodeList for ElementList { fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { match p.cur() { - T![]) && !p.at(EOF) { - p.bump_with_context(HTML_LITERAL, HtmlLexContext::Comment); - } - p.expect(T![-->]); - Present(m.complete(p, HTML_COMMENT)) -} - fn parse_cdata_section(p: &mut HtmlParser) -> ParsedSyntax { if !p.at(T![" ParsedSyntax { } let m = p.start(); let range = p.cur_range(); - p.bump_with_context(T!["{{"], HtmlLexContext::OutsideTag); + p.bump(T!["{{"]); while p.at(HTML_LITERAL) { - p.bump_with_context(HTML_LITERAL, HtmlLexContext::OutsideTag); + p.bump(HTML_LITERAL); } if p.at(T![<]) && !p.at(EOF) { p.error( @@ -375,7 +397,7 @@ fn parse_text_expression(p: &mut HtmlParser) -> ParsedSyntax { m.abandon(p); return Absent; } - p.expect_with_context(T!["}}"], HtmlLexContext::OutsideTag); + p.expect(T!["}}"]); Present(m.complete(p, HTML_TEXT_EXPRESSION)) } diff --git a/crates/biome_html_parser/src/token_source.rs b/crates/biome_html_parser/src/token_source.rs index 0621b7f273da..fffa2145235b 100644 --- a/crates/biome_html_parser/src/token_source.rs +++ b/crates/biome_html_parser/src/token_source.rs @@ -16,13 +16,15 @@ pub(crate) struct HtmlTokenSource<'source> { #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum HtmlLexContext { - /// The default state. This state is used for a majority of the lexing, which is inside html tags. - #[default] - Regular, + /// The default state. This state is used for lexing outside of tags. + /// /// When the lexer is outside of a tag, special characters are lexed as text. /// /// The exeptions being `<` which indicates the start of a tag, and `>` which is invalid syntax if not preceeded with a `<`. - OutsideTag, + #[default] + Regular, + /// When the lexer is inside a tag, special characters are lexed as tag tokens. + InsideTag, /// When the parser encounters a `=` token (the beginning of the attribute initializer clause), it switches to this context. /// /// This is because attribute values can start and end with a `"` or `'` character, or be unquoted, and the lexer needs to know to start lexing a string literal. @@ -33,8 +35,6 @@ pub(crate) enum HtmlLexContext { Doctype, /// Treat everything as text until the closing tag is encountered. EmbeddedLanguage(HtmlEmbeddedLanguage), - /// Comments are treated as text until the closing comment tag is encountered. - Comment, /// CDATA Sections are treated as text until the closing CDATA token is encountered. CdataSection, /// Lexing the Astro frontmatter diff --git a/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unqouted-value1.html.snap b/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unqouted-value1.html.snap index 6fcdc04f86f3..82498c242462 100644 --- a/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unqouted-value1.html.snap +++ b/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unqouted-value1.html.snap @@ -53,18 +53,14 @@ HtmlRoot { HtmlBogusElement { items: [ ERROR_TOKEN@18..20 "=" [] [Whitespace(" ")], + HTML_LITERAL@20..24 ">foo" [] [], ], }, ], }, - R_ANGLE@20..21 ">" [] [], ], }, - HtmlElementList [ - HtmlContent { - value_token: HTML_LITERAL@21..24 "foo" [] [], - }, - ], + HtmlElementList [], HtmlClosingElement { l_angle_token: L_ANGLE@24..25 "<" [] [], slash_token: SLASH@25..26 "/" [] [], @@ -97,18 +93,14 @@ HtmlRoot { HtmlBogusElement { items: [ ERROR_TOKEN@43..45 "?" [] [Whitespace(" ")], + HTML_LITERAL@45..49 ">foo" [] [], ], }, ], }, - R_ANGLE@45..46 ">" [] [], ], }, - HtmlElementList [ - HtmlContent { - value_token: HTML_LITERAL@46..49 "foo" [] [], - }, - ], + HtmlElementList [], HtmlClosingElement { l_angle_token: L_ANGLE@49..50 "<" [] [], slash_token: SLASH@50..51 "/" [] [], @@ -151,23 +143,21 @@ HtmlRoot { 3: R_ANGLE@4..5 ">" [] [] 1: HTML_ELEMENT_LIST@5..55 0: HTML_BOGUS_ELEMENT@5..30 - 0: HTML_BOGUS@5..21 + 0: HTML_BOGUS@5..24 0: L_ANGLE@5..8 "<" [Newline("\n"), Whitespace("\t")] [] 1: HTML_TAG_NAME@8..12 0: HTML_LITERAL@8..12 "div" [] [Whitespace(" ")] - 2: HTML_BOGUS@12..20 + 2: HTML_BOGUS@12..24 0: HTML_ATTRIBUTE@12..18 0: HTML_ATTRIBUTE_NAME@12..17 0: HTML_LITERAL@12..17 "class" [] [] 1: HTML_ATTRIBUTE_INITIALIZER_CLAUSE@17..18 0: EQ@17..18 "=" [] [] 1: (empty) - 1: HTML_BOGUS_ELEMENT@18..20 + 1: HTML_BOGUS_ELEMENT@18..24 0: ERROR_TOKEN@18..20 "=" [] [Whitespace(" ")] - 3: R_ANGLE@20..21 ">" [] [] - 1: HTML_ELEMENT_LIST@21..24 - 0: HTML_CONTENT@21..24 - 0: HTML_LITERAL@21..24 "foo" [] [] + 1: HTML_LITERAL@20..24 ">foo" [] [] + 1: HTML_ELEMENT_LIST@24..24 2: HTML_CLOSING_ELEMENT@24..30 0: L_ANGLE@24..25 "<" [] [] 1: SLASH@25..26 "/" [] [] @@ -175,23 +165,21 @@ HtmlRoot { 0: HTML_LITERAL@26..29 "div" [] [] 3: R_ANGLE@29..30 ">" [] [] 1: HTML_BOGUS_ELEMENT@30..55 - 0: HTML_BOGUS@30..46 + 0: HTML_BOGUS@30..49 0: L_ANGLE@30..33 "<" [Newline("\n"), Whitespace("\t")] [] 1: HTML_TAG_NAME@33..37 0: HTML_LITERAL@33..37 "div" [] [Whitespace(" ")] - 2: HTML_BOGUS@37..45 + 2: HTML_BOGUS@37..49 0: HTML_ATTRIBUTE@37..43 0: HTML_ATTRIBUTE_NAME@37..42 0: HTML_LITERAL@37..42 "class" [] [] 1: HTML_ATTRIBUTE_INITIALIZER_CLAUSE@42..43 0: EQ@42..43 "=" [] [] 1: (empty) - 1: HTML_BOGUS_ELEMENT@43..45 + 1: HTML_BOGUS_ELEMENT@43..49 0: ERROR_TOKEN@43..45 "?" [] [Whitespace(" ")] - 3: R_ANGLE@45..46 ">" [] [] - 1: HTML_ELEMENT_LIST@46..49 - 0: HTML_CONTENT@46..49 - 0: HTML_LITERAL@46..49 "foo" [] [] + 1: HTML_LITERAL@45..49 ">foo" [] [] + 1: HTML_ELEMENT_LIST@49..49 2: HTML_CLOSING_ELEMENT@49..55 0: L_ANGLE@49..50 "<" [] [] 1: SLASH@50..51 "/" [] [] @@ -231,6 +219,24 @@ invalid-unqouted-value1.html:2:13 parse ━━━━━━━━━━━━━ 3 │
foo
4 │
+invalid-unqouted-value1.html:2:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 1 │
+ > 2 │
foo
+ │ ^ + 3 │
foo
+ 4 │
+ + i Expected an attribute here. + + 1 │
+ > 2 │
foo
+ │ ^ + 3 │
foo
+ 4 │
+ invalid-unqouted-value1.html:3:13 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected character in unquoted attribute value @@ -253,4 +259,24 @@ invalid-unqouted-value1.html:3:13 parse ━━━━━━━━━━━━━ 4 │
5 │ +invalid-unqouted-value1.html:3:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 1 │
+ 2 │
foo
+ > 3 │
foo
+ │ ^ + 4 │
+ 5 │ + + i Expected an attribute here. + + 1 │
+ 2 │
foo
+ > 3 │
foo
+ │ ^ + 4 │
+ 5 │ + ``` diff --git a/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unquoted-value2.html.snap b/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unquoted-value2.html.snap index 09b5ca38a8cd..8c743db80739 100644 --- a/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unquoted-value2.html.snap +++ b/crates/biome_html_parser/tests/html_specs/error/attributes/invalid-unquoted-value2.html.snap @@ -53,19 +53,14 @@ HtmlRoot { HtmlBogusElement { items: [ ERROR_TOKEN@18..22 "foo\"" [] [], - HTML_LITERAL@22..26 "bar" [] [Whitespace(" ")], + HTML_LITERAL@22..30 "bar >foo" [] [], ], }, ], }, - R_ANGLE@26..27 ">" [] [], ], }, - HtmlElementList [ - HtmlContent { - value_token: HTML_LITERAL@27..30 "foo" [] [], - }, - ], + HtmlElementList [], HtmlClosingElement { l_angle_token: L_ANGLE@30..31 "<" [] [], slash_token: SLASH@31..32 "/" [] [], @@ -98,19 +93,14 @@ HtmlRoot { HtmlBogusElement { items: [ ERROR_TOKEN@49..53 "foo'" [] [], - HTML_LITERAL@53..57 "bar" [] [Whitespace(" ")], + HTML_LITERAL@53..61 "bar >foo" [] [], ], }, ], }, - R_ANGLE@57..58 ">" [] [], ], }, - HtmlElementList [ - HtmlContent { - value_token: HTML_LITERAL@58..61 "foo" [] [], - }, - ], + HtmlElementList [], HtmlClosingElement { l_angle_token: L_ANGLE@61..62 "<" [] [], slash_token: SLASH@62..63 "/" [] [], @@ -153,24 +143,21 @@ HtmlRoot { 3: R_ANGLE@4..5 ">" [] [] 1: HTML_ELEMENT_LIST@5..67 0: HTML_BOGUS_ELEMENT@5..36 - 0: HTML_BOGUS@5..27 + 0: HTML_BOGUS@5..30 0: L_ANGLE@5..8 "<" [Newline("\n"), Whitespace("\t")] [] 1: HTML_TAG_NAME@8..12 0: HTML_LITERAL@8..12 "div" [] [Whitespace(" ")] - 2: HTML_BOGUS@12..26 + 2: HTML_BOGUS@12..30 0: HTML_ATTRIBUTE@12..18 0: HTML_ATTRIBUTE_NAME@12..17 0: HTML_LITERAL@12..17 "class" [] [] 1: HTML_ATTRIBUTE_INITIALIZER_CLAUSE@17..18 0: EQ@17..18 "=" [] [] 1: (empty) - 1: HTML_BOGUS_ELEMENT@18..26 + 1: HTML_BOGUS_ELEMENT@18..30 0: ERROR_TOKEN@18..22 "foo\"" [] [] - 1: HTML_LITERAL@22..26 "bar" [] [Whitespace(" ")] - 3: R_ANGLE@26..27 ">" [] [] - 1: HTML_ELEMENT_LIST@27..30 - 0: HTML_CONTENT@27..30 - 0: HTML_LITERAL@27..30 "foo" [] [] + 1: HTML_LITERAL@22..30 "bar >foo" [] [] + 1: HTML_ELEMENT_LIST@30..30 2: HTML_CLOSING_ELEMENT@30..36 0: L_ANGLE@30..31 "<" [] [] 1: SLASH@31..32 "/" [] [] @@ -178,24 +165,21 @@ HtmlRoot { 0: HTML_LITERAL@32..35 "div" [] [] 3: R_ANGLE@35..36 ">" [] [] 1: HTML_BOGUS_ELEMENT@36..67 - 0: HTML_BOGUS@36..58 + 0: HTML_BOGUS@36..61 0: L_ANGLE@36..39 "<" [Newline("\n"), Whitespace("\t")] [] 1: HTML_TAG_NAME@39..43 0: HTML_LITERAL@39..43 "div" [] [Whitespace(" ")] - 2: HTML_BOGUS@43..57 + 2: HTML_BOGUS@43..61 0: HTML_ATTRIBUTE@43..49 0: HTML_ATTRIBUTE_NAME@43..48 0: HTML_LITERAL@43..48 "class" [] [] 1: HTML_ATTRIBUTE_INITIALIZER_CLAUSE@48..49 0: EQ@48..49 "=" [] [] 1: (empty) - 1: HTML_BOGUS_ELEMENT@49..57 + 1: HTML_BOGUS_ELEMENT@49..61 0: ERROR_TOKEN@49..53 "foo'" [] [] - 1: HTML_LITERAL@53..57 "bar" [] [Whitespace(" ")] - 3: R_ANGLE@57..58 ">" [] [] - 1: HTML_ELEMENT_LIST@58..61 - 0: HTML_CONTENT@58..61 - 0: HTML_LITERAL@58..61 "foo" [] [] + 1: HTML_LITERAL@53..61 "bar >foo" [] [] + 1: HTML_ELEMENT_LIST@61..61 2: HTML_CLOSING_ELEMENT@61..67 0: L_ANGLE@61..62 "<" [] [] 1: SLASH@62..63 "/" [] [] @@ -253,6 +237,24 @@ invalid-unquoted-value2.html:2:16 parse ━━━━━━━━━━━━━ 3 │
foo
4 │
+invalid-unquoted-value2.html:2:25 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 1 │
+ > 2 │
foo
+ │ ^ + 3 │
foo
+ 4 │
+ + i Expected an attribute here. + + 1 │
+ > 2 │
foo
+ │ ^ + 3 │
foo
+ 4 │
+ invalid-unquoted-value2.html:3:13 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an initializer but instead found 'foo''. @@ -295,4 +297,24 @@ invalid-unquoted-value2.html:3:16 parse ━━━━━━━━━━━━━ 4 │
5 │ +invalid-unquoted-value2.html:3:25 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an attribute but instead found '<'. + + 1 │
+ 2 │
foo
+ > 3 │
foo
+ │ ^ + 4 │
+ 5 │ + + i Expected an attribute here. + + 1 │
+ 2 │
foo
+ > 3 │
foo
+ │ ^ + 4 │
+ 5 │ + ``` diff --git a/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap index 2adae1b88fa4..44b5ec329a8a 100644 --- a/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap +++ b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap @@ -18,15 +18,8 @@ HtmlRoot { frontmatter: missing (optional), directive: missing (optional), html: HtmlElementList [ - HtmlElement { - opening_element: HtmlOpeningElement { - l_angle_token: L_ANGLE@0..1 "<" [] [], - name: missing (required), - attributes: HtmlAttributeList [], - r_angle_token: missing (required), - }, - children: HtmlElementList [], - closing_element: missing (required), + HtmlContent { + value_token: HTML_LITERAL@0..1 "<" [] [], }, ], eof_token: EOF@1..2 "" [Newline("\n")] [], @@ -41,14 +34,8 @@ HtmlRoot { 1: (empty) 2: (empty) 3: HTML_ELEMENT_LIST@0..1 - 0: HTML_ELEMENT@0..1 - 0: HTML_OPENING_ELEMENT@0..1 - 0: L_ANGLE@0..1 "<" [] [] - 1: (empty) - 2: HTML_ATTRIBUTE_LIST@1..1 - 3: (empty) - 1: HTML_ELEMENT_LIST@1..1 - 2: (empty) + 0: HTML_CONTENT@0..1 + 0: HTML_LITERAL@0..1 "<" [] [] 4: EOF@1..2 "" [Newline("\n")] [] ``` @@ -56,18 +43,14 @@ HtmlRoot { ## Diagnostics ``` -solo-no-tag-name.html:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +solo-no-tag-name.html:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × Expected an element name but instead found the end of the file. + × Unescaped `<` bracket character. Expected a tag or escaped character. - 1 │ < - > 2 │ - │ + > 1 │ < + │ ^ + 2 │ - i Expected an element name here. - - 1 │ < - > 2 │ - │ + i Replace this character with `<` to escape it. ``` diff --git a/crates/biome_html_parser/tests/html_specs/error/vue_unclosed_expression.vue.snap b/crates/biome_html_parser/tests/html_specs/error/vue_unclosed_expression.vue.snap index 6d1ced7b45e4..516f9016093c 100644 --- a/crates/biome_html_parser/tests/html_specs/error/vue_unclosed_expression.vue.snap +++ b/crates/biome_html_parser/tests/html_specs/error/vue_unclosed_expression.vue.snap @@ -37,8 +37,11 @@ HtmlRoot { HtmlContent { value_token: HTML_LITERAL@17..19 "<" [Newline("\n")] [], }, - HtmlContent { - value_token: HTML_LITERAL@19..29 "/template>" [] [], + HtmlBogusElement { + items: [ + SLASH@19..20 "/" [] [], + HTML_LITERAL@20..29 "template>" [] [], + ], }, ], }, @@ -69,8 +72,9 @@ HtmlRoot { 1: HTML_LITERAL@14..17 "xyz" [] [] 2: HTML_CONTENT@17..19 0: HTML_LITERAL@17..19 "<" [Newline("\n")] [] - 3: HTML_CONTENT@19..29 - 0: HTML_LITERAL@19..29 "/template>" [] [] + 3: HTML_BOGUS_ELEMENT@19..29 + 0: SLASH@19..20 "/" [] [] + 1: HTML_LITERAL@20..29 "template>" [] [] 4: EOF@29..30 "" [Newline("\n")] [] ``` @@ -96,6 +100,21 @@ vue_unclosed_expression.vue:3:1 parse ━━━━━━━━━━━━━━ 3 │ 4 │ +vue_unclosed_expression.vue:3:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 1 │ + │ ^^^^^^^^^^ + 4 │ + + i Expected one of: + + - element + - text + vue_unclosed_expression.vue:4:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a closing tag but instead found the end of the file. diff --git a/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html b/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html new file mode 100644 index 000000000000..0a0c15ac0a10 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html.snap b/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html.snap new file mode 100644 index 000000000000..f03e1696d8a1 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/comment-inline.html.snap @@ -0,0 +1,69 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html + + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + frontmatter: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlElement { + opening_element: HtmlOpeningElement { + l_angle_token: L_ANGLE@0..1 "<" [] [], + name: HtmlTagName { + value_token: HTML_LITERAL@1..5 "span" [] [], + }, + attributes: HtmlAttributeList [], + r_angle_token: R_ANGLE@5..26 ">" [] [Comments("")], + }, + children: HtmlElementList [], + closing_element: HtmlClosingElement { + l_angle_token: L_ANGLE@26..27 "<" [] [], + slash_token: SLASH@27..28 "/" [] [], + name: HtmlTagName { + value_token: HTML_LITERAL@28..32 "span" [] [], + }, + r_angle_token: R_ANGLE@32..33 ">" [] [], + }, + }, + ], + eof_token: EOF@33..34 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..34 + 0: (empty) + 1: (empty) + 2: (empty) + 3: HTML_ELEMENT_LIST@0..33 + 0: HTML_ELEMENT@0..33 + 0: HTML_OPENING_ELEMENT@0..26 + 0: L_ANGLE@0..1 "<" [] [] + 1: HTML_TAG_NAME@1..5 + 0: HTML_LITERAL@1..5 "span" [] [] + 2: HTML_ATTRIBUTE_LIST@5..5 + 3: R_ANGLE@5..26 ">" [] [Comments("")] + 1: HTML_ELEMENT_LIST@26..26 + 2: HTML_CLOSING_ELEMENT@26..33 + 0: L_ANGLE@26..27 "<" [] [] + 1: SLASH@27..28 "/" [] [] + 2: HTML_TAG_NAME@28..32 + 0: HTML_LITERAL@28..32 "span" [] [] + 3: R_ANGLE@32..33 ">" [] [] + 4: EOF@33..34 "" [Newline("\n")] [] + +``` 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 199307089601..24f65c8105b9 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 @@ -17,14 +17,8 @@ HtmlRoot { bom_token: missing (optional), frontmatter: missing (optional), directive: missing (optional), - html: HtmlElementList [ - HtmlComment { - comment_start_token: COMMENT_START@0..4 "" [] [], - }, - ], - eof_token: EOF@20..21 "" [Newline("\n")] [], + html: HtmlElementList [], + eof_token: EOF@0..21 "" [Comments(""), Newline("\n")] [], } ``` @@ -35,11 +29,7 @@ HtmlRoot { 0: (empty) 1: (empty) 2: (empty) - 3: HTML_ELEMENT_LIST@0..20 - 0: HTML_COMMENT@0..20 - 0: COMMENT_START@0..4 "" [] [] - 4: EOF@20..21 "" [Newline("\n")] [] + 3: HTML_ELEMENT_LIST@0..0 + 4: EOF@0..21 "" [Comments(""), Newline("\n")] [] ``` diff --git a/crates/biome_html_parser/tests/html_specs/ok/comment2.html b/crates/biome_html_parser/tests/html_specs/ok/comment2.html new file mode 100644 index 000000000000..bf736b1ac518 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/comment2.html @@ -0,0 +1,2 @@ + +123 diff --git a/crates/biome_html_parser/tests/html_specs/ok/comment2.html.snap b/crates/biome_html_parser/tests/html_specs/ok/comment2.html.snap new file mode 100644 index 000000000000..7744b4ff7914 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/comment2.html.snap @@ -0,0 +1,42 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html + +123 + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + frontmatter: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlContent { + value_token: HTML_LITERAL@0..24 "123" [Comments(""), Newline("\n")] [], + }, + ], + eof_token: EOF@24..25 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..25 + 0: (empty) + 1: (empty) + 2: (empty) + 3: HTML_ELEMENT_LIST@0..24 + 0: HTML_CONTENT@0..24 + 0: HTML_LITERAL@0..24 "123" [Comments(""), Newline("\n")] [] + 4: EOF@24..25 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html b/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html new file mode 100644 index 000000000000..33d937abdc72 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html @@ -0,0 +1,9 @@ +This document is meant to contain 2 text blocks. This is the first text block, +and the following will be the second text block. The content of this block is +intentionally long so that it line breaks. + +We define the separation of "blocks" of text to be 2 line breaks, or 1 empty +line between them. This is to make it easier for users to suppress formatting +for specific blocks, while still preserving whitespace sensitivity. Despite +this, the formatter can and will combine separate blocks of content. This is +just a concept for the parser to handle. diff --git a/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html.snap b/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html.snap new file mode 100644 index 000000000000..e1b2d2d306b5 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/content-blocks.html.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html +This document is meant to contain 2 text blocks. This is the first text block, +and the following will be the second text block. The content of this block is +intentionally long so that it line breaks. + +We define the separation of "blocks" of text to be 2 line breaks, or 1 empty +line between them. This is to make it easier for users to suppress formatting +for specific blocks, while still preserving whitespace sensitivity. Despite +this, the formatter can and will combine separate blocks of content. This is +just a concept for the parser to handle. + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + frontmatter: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlContent { + value_token: HTML_LITERAL@0..199 "This document is meant to contain 2 text blocks. This is the first text block,\nand the following will be the second text block. The content of this block is\nintentionally long so that it line breaks." [] [], + }, + HtmlContent { + value_token: HTML_LITERAL@199..549 "We define the separation of \"blocks\" of text to be 2 line breaks, or 1 empty\nline between them. This is to make it easier for users to suppress formatting\nfor specific blocks, while still preserving whitespace sensitivity. Despite\nthis, the formatter can and will combine separate blocks of content. This is\njust a concept for the parser to handle." [Newline("\n"), Newline("\n")] [], + }, + ], + eof_token: EOF@549..550 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..550 + 0: (empty) + 1: (empty) + 2: (empty) + 3: HTML_ELEMENT_LIST@0..549 + 0: HTML_CONTENT@0..199 + 0: HTML_LITERAL@0..199 "This document is meant to contain 2 text blocks. This is the first text block,\nand the following will be the second text block. The content of this block is\nintentionally long so that it line breaks." [] [] + 1: HTML_CONTENT@199..549 + 0: HTML_LITERAL@199..549 "We define the separation of \"blocks\" of text to be 2 line breaks, or 1 empty\nline between them. This is to make it easier for users to suppress formatting\nfor specific blocks, while still preserving whitespace sensitivity. Despite\nthis, the formatter can and will combine separate blocks of content. This is\njust a concept for the parser to handle." [Newline("\n"), Newline("\n")] [] + 4: EOF@549..550 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_html_parser/tests/html_specs/ok/element_list2.html.snap b/crates/biome_html_parser/tests/html_specs/ok/element_list2.html.snap index 8995ac887286..bd14371291f5 100644 --- a/crates/biome_html_parser/tests/html_specs/ok/element_list2.html.snap +++ b/crates/biome_html_parser/tests/html_specs/ok/element_list2.html.snap @@ -24,10 +24,7 @@ HtmlRoot { directive: missing (optional), html: HtmlElementList [ HtmlContent { - value_token: HTML_LITERAL@0..5 "some" [] [Whitespace(" ")], - }, - HtmlContent { - value_token: HTML_LITERAL@5..9 "text" [] [], + value_token: HTML_LITERAL@0..9 "some text" [] [], }, HtmlElement { opening_element: HtmlOpeningElement { @@ -121,11 +118,9 @@ HtmlRoot { 1: (empty) 2: (empty) 3: HTML_ELEMENT_LIST@0..67 - 0: HTML_CONTENT@0..5 - 0: HTML_LITERAL@0..5 "some" [] [Whitespace(" ")] - 1: HTML_CONTENT@5..9 - 0: HTML_LITERAL@5..9 "text" [] [] - 2: HTML_ELEMENT@9..21 + 0: HTML_CONTENT@0..9 + 0: HTML_LITERAL@0..9 "some text" [] [] + 1: HTML_ELEMENT@9..21 0: HTML_OPENING_ELEMENT@9..15 0: L_ANGLE@9..11 "<" [Newline("\n")] [] 1: HTML_TAG_NAME@11..14 @@ -139,9 +134,9 @@ HtmlRoot { 2: HTML_TAG_NAME@17..20 0: HTML_LITERAL@17..20 "div" [] [] 3: R_ANGLE@20..21 ">" [] [] - 3: HTML_CONTENT@21..31 + 2: HTML_CONTENT@21..31 0: HTML_LITERAL@21..31 "some text" [Newline("\n")] [] - 4: HTML_ELEMENT@31..43 + 3: HTML_ELEMENT@31..43 0: HTML_OPENING_ELEMENT@31..37 0: L_ANGLE@31..33 "<" [Newline("\n")] [] 1: HTML_TAG_NAME@33..36 @@ -155,7 +150,7 @@ HtmlRoot { 2: HTML_TAG_NAME@39..42 0: HTML_LITERAL@39..42 "div" [] [] 3: R_ANGLE@42..43 ">" [] [] - 5: HTML_ELEMENT@43..55 + 4: HTML_ELEMENT@43..55 0: HTML_OPENING_ELEMENT@43..49 0: L_ANGLE@43..45 "<" [Newline("\n")] [] 1: HTML_TAG_NAME@45..48 @@ -169,7 +164,7 @@ HtmlRoot { 2: HTML_TAG_NAME@51..54 0: HTML_LITERAL@51..54 "div" [] [] 3: R_ANGLE@54..55 ">" [] [] - 6: HTML_ELEMENT@55..67 + 5: HTML_ELEMENT@55..67 0: HTML_OPENING_ELEMENT@55..61 0: L_ANGLE@55..57 "<" [Newline("\n")] [] 1: HTML_TAG_NAME@57..60 diff --git a/crates/biome_html_parser/tests/html_specs/ok/interpolation.html.snap b/crates/biome_html_parser/tests/html_specs/ok/interpolation.html.snap index b90ce7220322..0d1b64f3c516 100644 --- a/crates/biome_html_parser/tests/html_specs/ok/interpolation.html.snap +++ b/crates/biome_html_parser/tests/html_specs/ok/interpolation.html.snap @@ -41,25 +41,16 @@ HtmlRoot { value_token: HTML_LITERAL@5..53 "Fuga magnam facilis. Voluptatem quaerat porro.{{" [] [], }, HtmlContent { - value_token: HTML_LITERAL@53..63 "x => {" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [], - }, - HtmlContent { - value_token: HTML_LITERAL@63..86 "const hello = 'world'" [Newline("\n"), Whitespace("\t")] [], - }, - HtmlContent { - value_token: HTML_LITERAL@86..101 "return hello;" [Newline("\n"), Whitespace("\t")] [], - }, - HtmlContent { - value_token: HTML_LITERAL@101..104 "}" [Newline("\n"), Whitespace("\t")] [], + value_token: HTML_LITERAL@53..104 "x => {\n\tconst hello = 'world'\n\treturn hello;\n\t}" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [], }, HtmlContent { value_token: HTML_LITERAL@104..111 "}}" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [Whitespace(" ")], }, HtmlContent { - value_token: HTML_LITERAL@111..158 "reprehenderit voluptates minus {{console.log(" [] [Whitespace(" ")], + value_token: HTML_LITERAL@111..125 "reprehenderit" [] [Whitespace(" ")], }, HtmlContent { - value_token: HTML_LITERAL@158..179 "short_interpolation )" [] [], + value_token: HTML_LITERAL@125..179 "voluptates minus {{console.log( short_interpolation )" [] [], }, HtmlContent { value_token: HTML_LITERAL@179..182 "}}" [] [Whitespace(" ")], @@ -100,23 +91,17 @@ HtmlRoot { 1: HTML_ELEMENT_LIST@5..187 0: HTML_CONTENT@5..53 0: HTML_LITERAL@5..53 "Fuga magnam facilis. Voluptatem quaerat porro.{{" [] [] - 1: HTML_CONTENT@53..63 - 0: HTML_LITERAL@53..63 "x => {" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [] - 2: HTML_CONTENT@63..86 - 0: HTML_LITERAL@63..86 "const hello = 'world'" [Newline("\n"), Whitespace("\t")] [] - 3: HTML_CONTENT@86..101 - 0: HTML_LITERAL@86..101 "return hello;" [Newline("\n"), Whitespace("\t")] [] - 4: HTML_CONTENT@101..104 - 0: HTML_LITERAL@101..104 "}" [Newline("\n"), Whitespace("\t")] [] - 5: HTML_CONTENT@104..111 + 1: HTML_CONTENT@53..104 + 0: HTML_LITERAL@53..104 "x => {\n\tconst hello = 'world'\n\treturn hello;\n\t}" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [] + 2: HTML_CONTENT@104..111 0: HTML_LITERAL@104..111 "}}" [Newline("\n"), Newline("\n"), Newline("\n"), Whitespace("\t")] [Whitespace(" ")] - 6: HTML_CONTENT@111..158 - 0: HTML_LITERAL@111..158 "reprehenderit voluptates minus {{console.log(" [] [Whitespace(" ")] - 7: HTML_CONTENT@158..179 - 0: HTML_LITERAL@158..179 "short_interpolation )" [] [] - 8: HTML_CONTENT@179..182 + 3: HTML_CONTENT@111..125 + 0: HTML_LITERAL@111..125 "reprehenderit" [] [Whitespace(" ")] + 4: HTML_CONTENT@125..179 + 0: HTML_LITERAL@125..179 "voluptates minus {{console.log( short_interpolation )" [] [] + 5: HTML_CONTENT@179..182 0: HTML_LITERAL@179..182 "}}" [] [Whitespace(" ")] - 9: HTML_CONTENT@182..187 + 6: HTML_CONTENT@182..187 0: HTML_LITERAL@182..187 "nemo." [] [] 2: HTML_CLOSING_ELEMENT@187..193 0: L_ANGLE@187..188 "<" [] [] diff --git a/crates/biome_html_parser/tests/html_specs/ok/no-end-tags/wbr.html.snap b/crates/biome_html_parser/tests/html_specs/ok/no-end-tags/wbr.html.snap index bdb33d331866..91bc685b4027 100644 --- a/crates/biome_html_parser/tests/html_specs/ok/no-end-tags/wbr.html.snap +++ b/crates/biome_html_parser/tests/html_specs/ok/no-end-tags/wbr.html.snap @@ -30,10 +30,7 @@ HtmlRoot { }, children: HtmlElementList [ HtmlContent { - value_token: HTML_LITERAL@3..48 "So then she pointed at the tiger and screamed" [] [], - }, - HtmlContent { - value_token: HTML_LITERAL@48..55 "there" [Newline("\n"), Whitespace("\t")] [], + value_token: HTML_LITERAL@3..55 "So then she pointed at the tiger and screamed\n\tthere" [] [], }, HtmlSelfClosingElement { l_angle_token: L_ANGLE@55..56 "<" [] [], @@ -186,99 +183,97 @@ HtmlRoot { 2: HTML_ATTRIBUTE_LIST@2..2 3: R_ANGLE@2..3 ">" [] [] 1: HTML_ELEMENT_LIST@3..137 - 0: HTML_CONTENT@3..48 - 0: HTML_LITERAL@3..48 "So then she pointed at the tiger and screamed" [] [] - 1: HTML_CONTENT@48..55 - 0: HTML_LITERAL@48..55 "there" [Newline("\n"), Whitespace("\t")] [] - 2: HTML_SELF_CLOSING_ELEMENT@55..60 + 0: HTML_CONTENT@3..55 + 0: HTML_LITERAL@3..55 "So then she pointed at the tiger and screamed\n\tthere" [] [] + 1: HTML_SELF_CLOSING_ELEMENT@55..60 0: L_ANGLE@55..56 "<" [] [] 1: HTML_TAG_NAME@56..59 0: HTML_LITERAL@56..59 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@59..59 3: (empty) 4: R_ANGLE@59..60 ">" [] [] - 3: HTML_CONTENT@60..62 + 2: HTML_CONTENT@60..62 0: HTML_LITERAL@60..62 "is" [] [] - 4: HTML_SELF_CLOSING_ELEMENT@62..67 + 3: HTML_SELF_CLOSING_ELEMENT@62..67 0: L_ANGLE@62..63 "<" [] [] 1: HTML_TAG_NAME@63..66 0: HTML_LITERAL@63..66 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@66..66 3: (empty) 4: R_ANGLE@66..67 ">" [] [] - 5: HTML_CONTENT@67..69 + 4: HTML_CONTENT@67..69 0: HTML_LITERAL@67..69 "no" [] [] - 6: HTML_SELF_CLOSING_ELEMENT@69..74 + 5: HTML_SELF_CLOSING_ELEMENT@69..74 0: L_ANGLE@69..70 "<" [] [] 1: HTML_TAG_NAME@70..73 0: HTML_LITERAL@70..73 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@73..73 3: (empty) 4: R_ANGLE@73..74 ">" [] [] - 7: HTML_CONTENT@74..77 + 6: HTML_CONTENT@74..77 0: HTML_LITERAL@74..77 "way" [] [] - 8: HTML_SELF_CLOSING_ELEMENT@77..82 + 7: HTML_SELF_CLOSING_ELEMENT@77..82 0: L_ANGLE@77..78 "<" [] [] 1: HTML_TAG_NAME@78..81 0: HTML_LITERAL@78..81 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@81..81 3: (empty) 4: R_ANGLE@81..82 ">" [] [] - 9: HTML_CONTENT@82..85 + 8: HTML_CONTENT@82..85 0: HTML_LITERAL@82..85 "you" [] [] - 10: HTML_SELF_CLOSING_ELEMENT@85..90 + 9: HTML_SELF_CLOSING_ELEMENT@85..90 0: L_ANGLE@85..86 "<" [] [] 1: HTML_TAG_NAME@86..89 0: HTML_LITERAL@86..89 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@89..89 3: (empty) 4: R_ANGLE@89..90 ">" [] [] - 11: HTML_CONTENT@90..93 + 10: HTML_CONTENT@90..93 0: HTML_LITERAL@90..93 "are" [] [] - 12: HTML_SELF_CLOSING_ELEMENT@93..98 + 11: HTML_SELF_CLOSING_ELEMENT@93..98 0: L_ANGLE@93..94 "<" [] [] 1: HTML_TAG_NAME@94..97 0: HTML_LITERAL@94..97 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@97..97 3: (empty) 4: R_ANGLE@97..98 ">" [] [] - 13: HTML_CONTENT@98..102 + 12: HTML_CONTENT@98..102 0: HTML_LITERAL@98..102 "ever" [] [] - 14: HTML_SELF_CLOSING_ELEMENT@102..107 + 13: HTML_SELF_CLOSING_ELEMENT@102..107 0: L_ANGLE@102..103 "<" [] [] 1: HTML_TAG_NAME@103..106 0: HTML_LITERAL@103..106 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@106..106 3: (empty) 4: R_ANGLE@106..107 ">" [] [] - 15: HTML_CONTENT@107..112 + 14: HTML_CONTENT@107..112 0: HTML_LITERAL@107..112 "going" [] [] - 16: HTML_SELF_CLOSING_ELEMENT@112..117 + 15: HTML_SELF_CLOSING_ELEMENT@112..117 0: L_ANGLE@112..113 "<" [] [] 1: HTML_TAG_NAME@113..116 0: HTML_LITERAL@113..116 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@116..116 3: (empty) 4: R_ANGLE@116..117 ">" [] [] - 17: HTML_CONTENT@117..119 + 16: HTML_CONTENT@117..119 0: HTML_LITERAL@117..119 "to" [] [] - 18: HTML_SELF_CLOSING_ELEMENT@119..124 + 17: HTML_SELF_CLOSING_ELEMENT@119..124 0: L_ANGLE@119..120 "<" [] [] 1: HTML_TAG_NAME@120..123 0: HTML_LITERAL@120..123 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@123..123 3: (empty) 4: R_ANGLE@123..124 ">" [] [] - 19: HTML_CONTENT@124..129 + 18: HTML_CONTENT@124..129 0: HTML_LITERAL@124..129 "catch" [] [] - 20: HTML_SELF_CLOSING_ELEMENT@129..134 + 19: HTML_SELF_CLOSING_ELEMENT@129..134 0: L_ANGLE@129..130 "<" [] [] 1: HTML_TAG_NAME@130..133 0: HTML_LITERAL@130..133 "wbr" [] [] 2: HTML_ATTRIBUTE_LIST@133..133 3: (empty) 4: R_ANGLE@133..134 ">" [] [] - 21: HTML_CONTENT@134..137 + 20: HTML_CONTENT@134..137 0: HTML_LITERAL@134..137 "me!" [] [] 2: HTML_CLOSING_ELEMENT@137..141 0: L_ANGLE@137..138 "<" [] [] diff --git a/crates/biome_html_parser/tests/html_specs/ok/special-chars.html.snap b/crates/biome_html_parser/tests/html_specs/ok/special-chars.html.snap index a8c720f2e934..9ee4f0d774b4 100644 --- a/crates/biome_html_parser/tests/html_specs/ok/special-chars.html.snap +++ b/crates/biome_html_parser/tests/html_specs/ok/special-chars.html.snap @@ -33,13 +33,7 @@ HtmlRoot { }, children: HtmlElementList [ HtmlContent { - value_token: HTML_LITERAL@5..17 "4 / 2 == 2" [Newline("\n"), Whitespace("\t")] [], - }, - HtmlContent { - value_token: HTML_LITERAL@17..24 "\"foo\"" [Newline("\n"), Whitespace("\t")] [], - }, - HtmlContent { - value_token: HTML_LITERAL@24..38 "html is cool" [Newline("\n"), Whitespace("\t")] [], + value_token: HTML_LITERAL@5..38 "4 / 2 == 2\n\t\"foo\"\n\thtml is cool" [Newline("\n"), Whitespace("\t")] [], }, ], closing_element: HtmlClosingElement { @@ -72,12 +66,8 @@ HtmlRoot { 2: HTML_ATTRIBUTE_LIST@4..4 3: R_ANGLE@4..5 ">" [] [] 1: HTML_ELEMENT_LIST@5..38 - 0: HTML_CONTENT@5..17 - 0: HTML_LITERAL@5..17 "4 / 2 == 2" [Newline("\n"), Whitespace("\t")] [] - 1: HTML_CONTENT@17..24 - 0: HTML_LITERAL@17..24 "\"foo\"" [Newline("\n"), Whitespace("\t")] [] - 2: HTML_CONTENT@24..38 - 0: HTML_LITERAL@24..38 "html is cool" [Newline("\n"), Whitespace("\t")] [] + 0: HTML_CONTENT@5..38 + 0: HTML_LITERAL@5..38 "4 / 2 == 2\n\t\"foo\"\n\thtml is cool" [Newline("\n"), Whitespace("\t")] [] 2: HTML_CLOSING_ELEMENT@38..45 0: L_ANGLE@38..40 "<" [Newline("\n")] [] 1: SLASH@40..41 "/" [] [] diff --git a/crates/biome_html_syntax/src/generated/kind.rs b/crates/biome_html_syntax/src/generated/kind.rs index 34d44dba1ed0..3cb0f980d1c7 100644 --- a/crates/biome_html_syntax/src/generated/kind.rs +++ b/crates/biome_html_syntax/src/generated/kind.rs @@ -18,8 +18,6 @@ pub enum HtmlSyntaxKind { EQ, BANG, MINUS, - COMMENT_START, - COMMENT_END, CDATA_START, CDATA_END, FENCE, @@ -55,8 +53,8 @@ pub enum HtmlSyntaxKind { HTML_ELEMENT_LIST, HTML_ATTRIBUTE_LIST, HTML_CONTENT, - HTML_COMMENT, HTML_CDATA_SECTION, + COMMENT, HTML_TEXT_EXPRESSION, SVELTE_TEXT_EXPRESSION, HTML_BOGUS, @@ -77,8 +75,6 @@ impl HtmlSyntaxKind { | EQ | BANG | MINUS - | COMMENT_START - | COMMENT_END | CDATA_START | CDATA_END | FENCE @@ -113,8 +109,6 @@ impl HtmlSyntaxKind { EQ => "=", BANG => "!", MINUS => "-", - COMMENT_START => "", CDATA_START => " "]]>", FENCE => "---", @@ -136,4 +130,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 } ; [] => { $ crate :: HtmlSyntaxKind :: COMMENT_END } ; [" { $ crate :: HtmlSyntaxKind :: CDATA_START } ; ["]]>"] => { $ crate :: HtmlSyntaxKind :: CDATA_END } ; [---] => { $ crate :: HtmlSyntaxKind :: FENCE } ; ['{'] => { $ crate :: HtmlSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: HtmlSyntaxKind :: R_CURLY } ; ["{{"] => { $ crate :: HtmlSyntaxKind :: L_DOUBLE_CURLY } ; ["}}"] => { $ crate :: HtmlSyntaxKind :: R_DOUBLE_CURLY } ; [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 :: CDATA_START } ; ["]]>"] => { $ crate :: HtmlSyntaxKind :: CDATA_END } ; [---] => { $ crate :: HtmlSyntaxKind :: FENCE } ; ['{'] => { $ crate :: HtmlSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: HtmlSyntaxKind :: R_CURLY } ; ["{{"] => { $ crate :: HtmlSyntaxKind :: L_DOUBLE_CURLY } ; ["}}"] => { $ crate :: HtmlSyntaxKind :: R_DOUBLE_CURLY } ; [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 5b03440910d1..3070d9ef3e19 100644 --- a/crates/biome_html_syntax/src/generated/macros.rs +++ b/crates/biome_html_syntax/src/generated/macros.rs @@ -41,10 +41,6 @@ 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 137d8c9352d7..a387e0c2615f 100644 --- a/crates/biome_html_syntax/src/generated/nodes.rs +++ b/crates/biome_html_syntax/src/generated/nodes.rs @@ -275,51 +275,6 @@ 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, } @@ -851,7 +806,6 @@ pub enum AnyHtmlElement { AnyHtmlContent(AnyHtmlContent), HtmlBogusElement(HtmlBogusElement), HtmlCdataSection(HtmlCdataSection), - HtmlComment(HtmlComment), HtmlElement(HtmlElement), HtmlSelfClosingElement(HtmlSelfClosingElement), } @@ -874,12 +828,6 @@ impl AnyHtmlElement { _ => None, } } - pub fn as_html_comment(&self) -> Option<&HtmlComment> { - match &self { - Self::HtmlComment(item) => Some(item), - _ => None, - } - } pub fn as_html_element(&self) -> Option<&HtmlElement> { match &self { Self::HtmlElement(item) => Some(item), @@ -1236,64 +1184,6 @@ 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 { - thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; - let current_depth = DEPTH.get(); - let result = if current_depth < 16 { - DEPTH.set(current_depth + 1); - 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() - } else { - f.debug_struct("HtmlComment").finish() - }; - DEPTH.set(current_depth); - result - } -} -impl From for SyntaxNode { - fn from(n: HtmlComment) -> Self { - n.syntax - } -} -impl From for SyntaxElement { - fn from(n: HtmlComment) -> Self { - n.syntax.into() - } -} impl AstNode for HtmlContent { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -2063,11 +1953,6 @@ impl From for AnyHtmlElement { Self::HtmlCdataSection(node) } } -impl From for AnyHtmlElement { - fn from(node: HtmlComment) -> Self { - Self::HtmlComment(node) - } -} impl From for AnyHtmlElement { fn from(node: HtmlElement) -> Self { Self::HtmlElement(node) @@ -2083,16 +1968,13 @@ impl AstNode for AnyHtmlElement { const KIND_SET: SyntaxKindSet = AnyHtmlContent::KIND_SET .union(HtmlBogusElement::KIND_SET) .union(HtmlCdataSection::KIND_SET) - .union(HtmlComment::KIND_SET) .union(HtmlElement::KIND_SET) .union(HtmlSelfClosingElement::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { match kind { - HTML_BOGUS_ELEMENT - | HTML_CDATA_SECTION - | HTML_COMMENT - | HTML_ELEMENT - | HTML_SELF_CLOSING_ELEMENT => true, + HTML_BOGUS_ELEMENT | HTML_CDATA_SECTION | HTML_ELEMENT | HTML_SELF_CLOSING_ELEMENT => { + true + } k if AnyHtmlContent::can_cast(k) => true, _ => false, } @@ -2101,7 +1983,6 @@ impl AstNode for AnyHtmlElement { let res = match syntax.kind() { HTML_BOGUS_ELEMENT => Self::HtmlBogusElement(HtmlBogusElement { syntax }), HTML_CDATA_SECTION => Self::HtmlCdataSection(HtmlCdataSection { syntax }), - HTML_COMMENT => Self::HtmlComment(HtmlComment { syntax }), HTML_ELEMENT => Self::HtmlElement(HtmlElement { syntax }), HTML_SELF_CLOSING_ELEMENT => { Self::HtmlSelfClosingElement(HtmlSelfClosingElement { syntax }) @@ -2119,7 +2000,6 @@ impl AstNode for AnyHtmlElement { match self { Self::HtmlBogusElement(it) => &it.syntax, Self::HtmlCdataSection(it) => &it.syntax, - Self::HtmlComment(it) => &it.syntax, Self::HtmlElement(it) => &it.syntax, Self::HtmlSelfClosingElement(it) => &it.syntax, Self::AnyHtmlContent(it) => it.syntax(), @@ -2129,7 +2009,6 @@ impl AstNode for AnyHtmlElement { match self { Self::HtmlBogusElement(it) => it.syntax, Self::HtmlCdataSection(it) => it.syntax, - Self::HtmlComment(it) => it.syntax, Self::HtmlElement(it) => it.syntax, Self::HtmlSelfClosingElement(it) => it.syntax, Self::AnyHtmlContent(it) => it.into_syntax(), @@ -2142,7 +2021,6 @@ impl std::fmt::Debug for AnyHtmlElement { Self::AnyHtmlContent(it) => std::fmt::Debug::fmt(it, f), Self::HtmlBogusElement(it) => std::fmt::Debug::fmt(it, f), Self::HtmlCdataSection(it) => std::fmt::Debug::fmt(it, f), - Self::HtmlComment(it) => std::fmt::Debug::fmt(it, f), Self::HtmlElement(it) => std::fmt::Debug::fmt(it, f), Self::HtmlSelfClosingElement(it) => std::fmt::Debug::fmt(it, f), } @@ -2154,7 +2032,6 @@ impl From for SyntaxNode { AnyHtmlElement::AnyHtmlContent(it) => it.into(), AnyHtmlElement::HtmlBogusElement(it) => it.into(), AnyHtmlElement::HtmlCdataSection(it) => it.into(), - AnyHtmlElement::HtmlComment(it) => it.into(), AnyHtmlElement::HtmlElement(it) => it.into(), AnyHtmlElement::HtmlSelfClosingElement(it) => it.into(), } @@ -2281,11 +2158,6 @@ 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 41188a3873b2..9aa643368c91 100644 --- a/crates/biome_html_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_html_syntax/src/generated/nodes_mut.rs @@ -105,26 +105,6 @@ 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 83293e87ec40..3061eaae149e 100644 --- a/crates/biome_html_syntax/src/lib.rs +++ b/crates/biome_html_syntax/src/lib.rs @@ -32,7 +32,7 @@ impl From for u16 { impl HtmlSyntaxKind { pub fn is_comments(self) -> bool { - matches!(self, Self::HTML_COMMENT) + matches!(self, Self::COMMENT) } #[inline] @@ -102,7 +102,7 @@ impl TryFrom for TriviaPieceKind { } } else if value.is_comments() { match value { - HtmlSyntaxKind::HTML_COMMENT => Ok(Self::SingleLineComment), + HtmlSyntaxKind::COMMENT => Ok(Self::SingleLineComment), _ => unreachable!("Not Comment"), } } else { diff --git a/crates/biome_parser/src/lib.rs b/crates/biome_parser/src/lib.rs index 1a518bd60bb9..c5ea95222712 100644 --- a/crates/biome_parser/src/lib.rs +++ b/crates/biome_parser/src/lib.rs @@ -293,6 +293,18 @@ pub trait Parser: Sized { self.do_bump(kind); } + /// Bumps the current token regardless of its kind and advances to the next token using + /// the new context. + fn bump_any_with_context(&mut self, context: ::Context) + where + Self::Source: BumpWithContext, + { + let kind = self.cur(); + assert_ne!(kind, Self::Kind::EOF); + + self.do_bump_with_context(kind, context); + } + /// Consumes the current token if `kind` matches and lexes the next token using the /// specified `context. fn bump_with_context( diff --git a/xtask/codegen/html.ungram b/xtask/codegen/html.ungram index 326144ef7501..b39b5661a813 100644 --- a/xtask/codegen/html.ungram +++ b/xtask/codegen/html.ungram @@ -70,7 +70,6 @@ AnyHtmlElement = HtmlSelfClosingElement | HtmlElement | AnyHtmlContent - | HtmlComment | HtmlCdataSection | HtmlBogusElement @@ -126,12 +125,6 @@ HtmlClosingElement = name: HtmlTagName '>' -// -HtmlComment = - '' - // // Reference: https://html.spec.whatwg.org/multipage/syntax.html#cdata-sections HtmlCdataSection = diff --git a/xtask/codegen/src/html_kinds_src.rs b/xtask/codegen/src/html_kinds_src.rs index 37c41db44c2c..8c5e89bce0b7 100644 --- a/xtask/codegen/src/html_kinds_src.rs +++ b/xtask/codegen/src/html_kinds_src.rs @@ -8,8 +8,6 @@ pub const HTML_KINDS_SRC: KindsSrc = KindsSrc { ("=", "EQ"), ("!", "BANG"), ("-", "MINUS"), - ("", "COMMENT_END"), ("", "CDATA_END"), ("---", "FENCE"), @@ -44,8 +42,8 @@ pub const HTML_KINDS_SRC: KindsSrc = KindsSrc { "HTML_ELEMENT_LIST", "HTML_ATTRIBUTE_LIST", "HTML_CONTENT", - "HTML_COMMENT", "HTML_CDATA_SECTION", + "COMMENT", "HTML_TEXT_EXPRESSION", "SVELTE_TEXT_EXPRESSION", // Bogus nodes