Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/proud-ends-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@biomejs/biome": minor
---

Added support for the top-level suppression comment `biome-ignore-all format: <explanation>`.

When the comment `biome-ignore-all format: <explanation>` is placed at the beginning of the document, Biome won't format the code.

The feature works for all supported languages. In the following JavaScript snippet, the code isn't formatted and will stay as is.

```js
// biome-ignore-all format: generated

const a = [ ]


const a = [ ]


const a = [ ]
```
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 41 additions & 6 deletions crates/biome_css_formatter/src/comments.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::prelude::*;
use biome_css_syntax::{
AnyCssDeclarationName, CssComplexSelector, CssFunction, CssIdentifier, CssLanguage,
CssSyntaxKind, TextLen,
AnyCssDeclarationName, AnyCssRoot, CssComplexSelector, CssFunction, CssIdentifier, CssLanguage,
CssSyntaxKind, TextLen, TextSize,
};
use biome_diagnostics::category;
use biome_formatter::comments::{
Expand All @@ -11,7 +11,7 @@ use biome_formatter::comments::{
use biome_formatter::formatter::Formatter;
use biome_formatter::{FormatResult, FormatRule, write};
use biome_rowan::SyntaxTriviaPieceComments;
use biome_suppression::parse_suppression_comment;
use biome_suppression::{SuppressionKind, parse_suppression_comment};

pub type CssComments = Comments<CssLanguage>;

Expand Down Expand Up @@ -68,6 +68,15 @@ impl CommentStyle for CssCommentStyle {
fn is_suppression(text: &str) -> bool {
parse_suppression_comment(text)
.filter_map(Result::ok)
.filter(|suppression| suppression.kind == SuppressionKind::Classic)
.flat_map(|suppression| suppression.categories)
.any(|(key, ..)| key == category!("format"))
}

fn is_global_suppression(text: &str) -> bool {
parse_suppression_comment(text)
.filter_map(Result::ok)
.filter(|suppression| suppression.kind == SuppressionKind::All)
.flat_map(|suppression| suppression.categories)
.any(|(key, ..)| key == category!("format"))
}
Expand All @@ -91,13 +100,16 @@ impl CommentStyle for CssCommentStyle {
match comment.text_position() {
CommentTextPosition::EndOfLine => handle_function_comment(comment)
.or_else(handle_declaration_name_comment)
.or_else(handle_complex_selector_comment),
.or_else(handle_complex_selector_comment)
.or_else(handle_global_suppression),
CommentTextPosition::OwnLine => handle_function_comment(comment)
.or_else(handle_declaration_name_comment)
.or_else(handle_complex_selector_comment),
.or_else(handle_complex_selector_comment)
.or_else(handle_global_suppression),
CommentTextPosition::SameLine => handle_function_comment(comment)
.or_else(handle_declaration_name_comment)
.or_else(handle_complex_selector_comment),
.or_else(handle_complex_selector_comment)
.or_else(handle_global_suppression),
}
}
}
Expand Down Expand Up @@ -148,3 +160,26 @@ fn handle_complex_selector_comment(
}
CommentPlacement::Default(comment)
}

fn handle_global_suppression(
comment: DecoratedComment<CssLanguage>,
) -> CommentPlacement<CssLanguage> {
let node = comment.enclosing_node();

if node.text_range_with_trivia().start() == TextSize::from(0) {
let has_global_suppression = node.first_leading_trivia().is_some_and(|trivia| {
trivia
.pieces()
.filter(|piece| piece.is_comments())
.any(|piece| CssCommentStyle::is_global_suppression(piece.text()))
});
let root = node.ancestors().find_map(AnyCssRoot::cast);
if let Some(root) = root
&& has_global_suppression
{
return CommentPlacement::leading(root.syntax().clone(), comment);
}
}

CommentPlacement::Default(comment)
}
7 changes: 6 additions & 1 deletion crates/biome_css_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ where
N: AstNode<Language = CssLanguage>,
{
fn fmt(&self, node: &N, f: &mut CssFormatter) -> FormatResult<()> {
if self.is_suppressed(node, f) {
if self.is_suppressed(node, f) || self.is_global_suppressed(node, f) {
return write!(f, [format_suppressed_node(node.syntax())]);
}

Expand All @@ -207,6 +207,11 @@ where
f.context().comments().is_suppressed(node.syntax())
}

/// Returns `true` if the node has a global suppression comment and should use the same formatting as in the source document.
fn is_global_suppressed(&self, node: &N, f: &CssFormatter) -> bool {
f.context().comments().is_global_suppressed(node.syntax())
}

/// Formats the [leading comments](biome_formatter::comments#leading-comments) of the node.
///
/// You may want to override this method if you want to manually handle the formatting of comments
Expand Down
10 changes: 10 additions & 0 deletions crates/biome_css_formatter/tests/specs/css/global_suppression.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* biome-ignore-all format: test */

.class {
scroll-snap-type:

x mandatory;
}

#exampleInputEmail1 { color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
info: css/global_suppression.css
---
# Input

```css
/* biome-ignore-all format: test */

.class {
scroll-snap-type:

x mandatory;
}

#exampleInputEmail1 { color: red;
}

```


=============================

# Outputs

## Output 1

-----
Indent style: Tab
Indent width: 2
Line ending: LF
Line width: 80
Quote style: Double Quotes
-----

```css
/* biome-ignore-all format: test */

.class {
scroll-snap-type:

x mandatory;
}

#exampleInputEmail1 { color: red;
}```
33 changes: 28 additions & 5 deletions crates/biome_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,10 @@ pub trait CommentStyle: Default {
false
}

fn is_global_suppression(_text: &str) -> bool {
false
}

/// Returns the (kind)[CommentKind] of the comment
fn get_comment_kind(comment: &SyntaxTriviaPieceComments<Self::Language>) -> CommentKind;

Expand Down Expand Up @@ -822,8 +826,8 @@ impl<L: Language> Comments<L> {
Self {
data: Rc::new(CommentsData {
root: Some(root.clone()),
is_suppression: Style::is_suppression,

is_node_suppression: Style::is_suppression,
is_global_suppression: Style::is_global_suppression,
comments,
with_skipped: skipped,
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -925,12 +929,27 @@ impl<L: Language> Comments<L> {
/// call expression is nested inside of the expression statement.
pub fn is_suppressed(&self, node: &SyntaxNode<L>) -> bool {
self.mark_suppression_checked(node);
let is_suppression = self.data.is_suppression;
let is_suppression = self.data.is_node_suppression;

self.leading_dangling_trailing_comments(node)
.any(|comment| is_suppression(comment.piece().text()))
}

pub fn is_global_suppressed(&self, node: &SyntaxNode<L>) -> bool {
self.mark_suppression_checked(node);
let start = node.text_range_with_trivia().start();
// global suppression comments must start at the beginning of the file
if start >= TextSize::from(0) {
let is_global_suppression = self.data.is_global_suppression;
// only leading comments can be global suppression comments
return self
.leading_comments(node)
.iter()
.any(|comment| is_global_suppression(comment.piece().text()));
}
false
}

#[cfg(not(debug_assertions))]
#[inline(always)]
pub fn mark_suppression_checked(&self, _: &SyntaxNode<L>) {}
Expand Down Expand Up @@ -1031,8 +1050,11 @@ Node:

struct CommentsData<L: Language> {
root: Option<SyntaxNode<L>>,
/// Returns true if the comment is node suppression
is_node_suppression: fn(&str) -> bool,

is_suppression: fn(&str) -> bool,
/// Returns true if the comment is global suppression
is_global_suppression: fn(&str) -> bool,

/// Stores all leading node comments by node
comments: CommentsMap<SyntaxElementKey, SourceComment<L>>,
Expand All @@ -1054,7 +1076,8 @@ impl<L: Language> Default for CommentsData<L> {
fn default() -> Self {
Self {
root: None,
is_suppression: |_| false,
is_node_suppression: |_| false,
is_global_suppression: |_| false,
comments: Default::default(),
with_skipped: Default::default(),
#[cfg(debug_assertions)]
Expand Down
4 changes: 4 additions & 0 deletions crates/biome_formatter/src/comments/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,10 @@ b;"#;
false
}

fn is_global_suppression(_: &str) -> bool {
false
}

fn get_comment_kind(_: &SyntaxTriviaPieceComments<Self::Language>) -> CommentKind {
CommentKind::Block
}
Expand Down
51 changes: 47 additions & 4 deletions crates/biome_graphql_formatter/src/comments.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::prelude::*;
use biome_diagnostics::category;
use biome_formatter::comments::{
CommentKind, CommentStyle, Comments, SourceComment, is_doc_comment,
CommentKind, CommentPlacement, CommentStyle, CommentTextPosition, Comments, DecoratedComment,
SourceComment, is_doc_comment,
};
use biome_formatter::formatter::Formatter;
use biome_formatter::{FormatResult, FormatRule, write};
use biome_graphql_syntax::{GraphqlLanguage, TextLen};
use biome_rowan::SyntaxTriviaPieceComments;
use biome_suppression::parse_suppression_comment;
use biome_graphql_syntax::{GraphqlLanguage, GraphqlRoot, TextLen};
use biome_rowan::{SyntaxTriviaPieceComments, TextSize};
use biome_suppression::{SuppressionKind, parse_suppression_comment};

pub type GraphqlComments = Comments<GraphqlLanguage>;

Expand Down Expand Up @@ -64,11 +65,53 @@ impl CommentStyle for GraphqlCommentStyle {
fn is_suppression(text: &str) -> bool {
parse_suppression_comment(text)
.filter_map(Result::ok)
.filter(|suppression| suppression.kind == SuppressionKind::Classic)
.flat_map(|suppression| suppression.categories)
.any(|(key, ..)| key == category!("format"))
}

fn is_global_suppression(text: &str) -> bool {
parse_suppression_comment(text)
.filter_map(Result::ok)
.filter(|suppression| suppression.kind == SuppressionKind::All)
.flat_map(|suppression| suppression.categories)
.any(|(key, ..)| key == category!("format"))
}

fn get_comment_kind(_comment: &SyntaxTriviaPieceComments<Self::Language>) -> CommentKind {
CommentKind::Line
}

fn place_comment(
&self,
comment: DecoratedComment<Self::Language>,
) -> CommentPlacement<Self::Language> {
match comment.text_position() {
CommentTextPosition::EndOfLine => handle_global_suppression(comment),
CommentTextPosition::OwnLine => handle_global_suppression(comment),
CommentTextPosition::SameLine => CommentPlacement::Default(comment),
}
}
}

fn handle_global_suppression(
comment: DecoratedComment<GraphqlLanguage>,
) -> CommentPlacement<GraphqlLanguage> {
let node = comment.enclosing_node();

if node.text_range_with_trivia().start() == TextSize::from(0) {
let has_global_suppression = node.first_leading_trivia().is_some_and(|trivia| {
trivia
.pieces()
.filter(|piece| piece.is_comments())
.any(|piece| GraphqlCommentStyle::is_global_suppression(piece.text()))
});
let root = node.ancestors().find_map(GraphqlRoot::cast);
if let Some(root) = root
&& has_global_suppression
{
return CommentPlacement::leading(root.syntax().clone(), comment);
}
}
CommentPlacement::Default(comment)
}
7 changes: 6 additions & 1 deletion crates/biome_graphql_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ where
N: AstNode<Language = GraphqlLanguage>,
{
fn fmt(&self, node: &N, f: &mut GraphqlFormatter) -> FormatResult<()> {
if self.is_suppressed(node, f) {
if self.is_suppressed(node, f) || self.is_global_suppressed(node, f) {
return write!(f, [format_suppressed_node(node.syntax())]);
}

Expand All @@ -201,6 +201,11 @@ where
f.context().comments().is_suppressed(node.syntax())
}

/// Returns `true` if the node has a global suppression comment and should use the same formatting as in the source document.
fn is_global_suppressed(&self, node: &N, f: &GraphqlFormatter) -> bool {
f.context().comments().is_global_suppressed(node.syntax())
}

/// Formats the [leading comments](biome_formatter::comments#leading-comments) of the node.
///
/// You may want to override this method if you want to manually handle the formatting of comments
Expand Down
1 change: 1 addition & 0 deletions crates/biome_graphql_formatter/src/verbatim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl Format<GraphqlFormatContext> for FormatGraphqlVerbatimNode<'_> {
// Format all leading comments that are outside of the node's source range.
if self.format_comments {
let comments = f.context().comments().clone();

let leading_comments = comments.leading_comments(self.node);

let outside_trimmed_range = leading_comments.partition_point(|comment| {
Expand Down
Loading
Loading