Skip to content

Commit deaaf94

Browse files
committed
feat(format/html): implement suppression comments
1 parent 8227773 commit deaaf94

File tree

72 files changed

+1417
-1162
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1417
-1162
lines changed

crates/biome_formatter/src/trivia.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ where
7878
let should_nestle =
7979
leading_comments_iter.peek().is_some_and(|next_comment| {
8080
should_nestle_adjacent_doc_comments(comment, next_comment)
81-
});
81+
}) || comment.piece().text().starts_with("<!--"); // HACK: we don't want to do this weird behavior to HTML comments.
8282

8383
write!(f, [maybe_space(!should_nestle)])?;
8484
}
@@ -140,7 +140,7 @@ where
140140

141141
let should_nestle = previous_comment.is_some_and(|previous_comment| {
142142
should_nestle_adjacent_doc_comments(previous_comment, comment)
143-
});
143+
}) || comment.piece().text().starts_with("<!--"); // HACK: we don't want to do this weird behavior to HTML comments.
144144

145145
// This allows comments at the end of nested structures:
146146
// {

crates/biome_formatter/src/verbatim.rs

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ pub struct FormatVerbatimNode<'node, L: Language> {
4141
format_comments: bool,
4242
}
4343

44+
impl<L: Language> FormatVerbatimNode<'_, L> {
45+
pub fn with_format_comments(mut self, format_comments: bool) -> Self {
46+
self.format_comments = format_comments;
47+
self
48+
}
49+
}
50+
4451
impl<Context> Format<Context> for FormatVerbatimNode<'_, Context::Language>
4552
where
4653
Context: CstFormatContext,

crates/biome_html_factory/src/generated/node_factory.rs

-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_html_factory/src/generated/syntax_factory.rs

-33
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_html_formatter/src/comments.rs

+73-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use biome_formatter::{
88
prelude::*,
99
write,
1010
};
11-
use biome_html_syntax::HtmlLanguage;
12-
use biome_rowan::{SyntaxTriviaPieceComments, TextLen};
11+
use biome_html_syntax::{HtmlClosingElement, HtmlLanguage, HtmlOpeningElement, HtmlSyntaxKind};
12+
use biome_rowan::{SyntaxNodeCast, SyntaxTriviaPieceComments, TextLen};
1313
use biome_suppression::parse_suppression_comment;
1414

1515
use crate::context::HtmlFormatContext;
@@ -96,10 +96,81 @@ impl CommentStyle for HtmlCommentStyle {
9696
CommentKind::Block
9797
}
9898

99+
/// This allows us to override which comments are associated with which nodes.
100+
///
101+
/// 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.
102+
///
103+
/// This method specifically, however, lets us fine tune which comments are associated with which nodes. This is useful when the default heuristic fails.
99104
fn place_comment(
100105
&self,
101106
comment: DecoratedComment<Self::Language>,
102107
) -> CommentPlacement<Self::Language> {
108+
// Fix trailing comments that are right before EOF being assigned to the wrong node.
109+
//
110+
// The issue is demonstrated in the example below.
111+
// ```html
112+
// Foo
113+
//
114+
// <!-- This comment gets assigned to the text node, despite it being actually attached to the EOF token. -->
115+
// ```
116+
if let Some(token) = comment.following_token() {
117+
if token.kind() == HtmlSyntaxKind::EOF {
118+
return CommentPlacement::trailing(comment.enclosing_node().clone(), comment);
119+
}
120+
}
121+
122+
// Fix trailing comments that should actually be leading comments for the next node.
123+
// ```html
124+
// 123<!--biome-ignore format: prettier ignore-->456
125+
// ```
126+
// This fix will ensure that the ignore comment is assigned to the 456 node instead of the 123 node.
127+
if let Some(following_node) = comment.following_node() {
128+
if comment.text_position().is_same_line() {
129+
return CommentPlacement::leading(following_node.clone(), comment);
130+
}
131+
}
132+
// match (comment.preceding_node(), comment.following_node()) {
133+
// (Some(preceding_node), Some(following_node)) => {
134+
// if preceding_node.kind() == HtmlSyntaxKind::HTML_CONTENT
135+
// && following_node.kind() == HtmlSyntaxKind::HTML_CONTENT
136+
// {
137+
// return CommentPlacement::leading(following_node.clone(), comment);
138+
// }
139+
140+
// if matches!(
141+
// following_node.kind(),
142+
// HtmlSyntaxKind::HTML_CONTENT
143+
// | HtmlSyntaxKind::HTML_ELEMENT
144+
// | HtmlSyntaxKind::HTML_SELF_CLOSING_ELEMENT
145+
// | HtmlSyntaxKind::HTML_BOGUS_ELEMENT
146+
// ) {
147+
// return CommentPlacement::leading(following_node.clone(), comment);
148+
// }
149+
// }
150+
// _ => {}
151+
// }
152+
153+
// move leading comments placed on closing tags to trailing tags of previous siblings, or to be dangling if no siblings are present.
154+
if let Some(_closing_tag) = comment
155+
.following_node()
156+
.and_then(|node| node.clone().cast::<HtmlClosingElement>())
157+
{
158+
if let Some(_preceding_opening_tag) = comment
159+
.preceding_node()
160+
.and_then(|node| node.clone().cast::<HtmlOpeningElement>())
161+
{
162+
return CommentPlacement::dangling(
163+
comment.preceding_node().unwrap().clone(),
164+
comment,
165+
);
166+
} else {
167+
return CommentPlacement::trailing(
168+
comment.preceding_node().unwrap().clone(),
169+
comment,
170+
);
171+
}
172+
}
173+
103174
CommentPlacement::Default(comment)
104175
}
105176
}

crates/biome_html_formatter/src/generated.rs

-38
Original file line numberDiff line numberDiff line change
@@ -189,44 +189,6 @@ impl IntoFormat<HtmlFormatContext> for biome_html_syntax::HtmlClosingElement {
189189
)
190190
}
191191
}
192-
impl FormatRule<biome_html_syntax::HtmlComment>
193-
for crate::html::auxiliary::comment::FormatHtmlComment
194-
{
195-
type Context = HtmlFormatContext;
196-
#[inline(always)]
197-
fn fmt(
198-
&self,
199-
node: &biome_html_syntax::HtmlComment,
200-
f: &mut HtmlFormatter,
201-
) -> FormatResult<()> {
202-
FormatNodeRule::<biome_html_syntax::HtmlComment>::fmt(self, node, f)
203-
}
204-
}
205-
impl AsFormat<HtmlFormatContext> for biome_html_syntax::HtmlComment {
206-
type Format<'a> = FormatRefWithRule<
207-
'a,
208-
biome_html_syntax::HtmlComment,
209-
crate::html::auxiliary::comment::FormatHtmlComment,
210-
>;
211-
fn format(&self) -> Self::Format<'_> {
212-
FormatRefWithRule::new(
213-
self,
214-
crate::html::auxiliary::comment::FormatHtmlComment::default(),
215-
)
216-
}
217-
}
218-
impl IntoFormat<HtmlFormatContext> for biome_html_syntax::HtmlComment {
219-
type Format = FormatOwnedWithRule<
220-
biome_html_syntax::HtmlComment,
221-
crate::html::auxiliary::comment::FormatHtmlComment,
222-
>;
223-
fn into_format(self) -> Self::Format {
224-
FormatOwnedWithRule::new(
225-
self,
226-
crate::html::auxiliary::comment::FormatHtmlComment::default(),
227-
)
228-
}
229-
}
230192
impl FormatRule<biome_html_syntax::HtmlContent>
231193
for crate::html::auxiliary::content::FormatHtmlContent
232194
{

crates/biome_html_formatter/src/html/any/element.rs

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ impl FormatRule<AnyHtmlElement> for FormatAnyHtmlElement {
1010
match node {
1111
AnyHtmlElement::HtmlBogusElement(node) => node.format().fmt(f),
1212
AnyHtmlElement::HtmlCdataSection(node) => node.format().fmt(f),
13-
AnyHtmlElement::HtmlComment(node) => node.format().fmt(f),
1413
AnyHtmlElement::HtmlContent(node) => node.format().fmt(f),
1514
AnyHtmlElement::HtmlElement(node) => node.format().fmt(f),
1615
AnyHtmlElement::HtmlSelfClosingElement(node) => node.format().fmt(f),

crates/biome_html_formatter/src/html/auxiliary/comment.rs

-10
This file was deleted.

crates/biome_html_formatter/src/html/auxiliary/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pub(crate) mod attribute_initializer_clause;
55
pub(crate) mod attribute_name;
66
pub(crate) mod cdata_section;
77
pub(crate) mod closing_element;
8-
pub(crate) mod comment;
98
pub(crate) mod content;
109
pub(crate) mod directive;
1110
pub(crate) mod element;
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
use crate::prelude::*;
22
use biome_formatter::write;
3-
use biome_html_syntax::HtmlRoot;
3+
use biome_html_syntax::{HtmlRoot, HtmlRootFields};
44
#[derive(Debug, Clone, Default)]
55
pub(crate) struct FormatHtmlRoot;
66
impl FormatNodeRule<HtmlRoot> for FormatHtmlRoot {
77
fn fmt_fields(&self, node: &HtmlRoot, f: &mut HtmlFormatter) -> FormatResult<()> {
8-
if let Some(bom) = node.bom_token() {
9-
bom.format().fmt(f)?;
10-
}
11-
if let Some(directive) = node.directive() {
12-
directive.format().fmt(f)?;
13-
}
8+
let HtmlRootFields {
9+
bom_token,
10+
directive,
11+
html,
12+
eof_token,
13+
} = node.as_fields();
1414

15-
node.html().format().fmt(f)?;
16-
17-
if let Ok(eof) = node.eof_token() {
18-
eof.format().fmt(f)?;
19-
}
20-
write!(f, [hard_line_break()])?;
21-
22-
Ok(())
15+
write!(
16+
f,
17+
[
18+
bom_token.format(),
19+
directive.format(),
20+
html.format(),
21+
hard_line_break(),
22+
format_removed(&eof_token?),
23+
]
24+
)
2325
}
2426
}

0 commit comments

Comments
 (0)