From 767674802d9f11e6b79eb17d44963430b27ecb7c Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 28 Jul 2025 17:33:39 +0100 Subject: [PATCH 1/2] refactor(format): use specialised tokens --- .../src/css/value/number.rs | 2 +- .../src/css/value/regular_dimension.rs | 5 +- crates/biome_css_formatter/src/lib.rs | 20 +- crates/biome_css_formatter/src/prelude.rs | 2 +- crates/biome_css_formatter/src/separated.rs | 17 +- crates/biome_css_formatter/src/trivia.rs | 78 ++++++ .../src/utils/string_utils.rs | 1 - crates/biome_formatter/src/comments.rs | 10 +- crates/biome_formatter/src/lib.rs | 39 --- crates/biome_formatter/src/prelude.rs | 5 +- crates/biome_formatter/src/separated.rs | 45 +++- crates/biome_formatter/src/token/number.rs | 42 +-- crates/biome_formatter/src/trivia.rs | 252 ++++++++++++------ .../graphql/auxiliary/union_member_types.rs | 8 +- .../definitions/directive_definition.rs | 4 +- .../graphql/lists/union_member_type_list.rs | 4 +- .../src/graphql/value/string_value.rs | 4 +- crates/biome_graphql_formatter/src/lib.rs | 41 ++- crates/biome_graphql_formatter/src/prelude.rs | 2 +- crates/biome_graphql_formatter/src/trivia.rs | 20 ++ crates/biome_grit_formatter/src/cst.rs | 36 ++- crates/biome_grit_formatter/src/lib.rs | 5 +- crates/biome_grit_formatter/src/prelude.rs | 2 +- crates/biome_grit_formatter/src/separated.rs | 20 +- crates/biome_grit_formatter/src/trivia.rs | 20 ++ crates/biome_html_formatter/src/lib.rs | 38 ++- crates/biome_html_formatter/src/prelude.rs | 2 +- crates/biome_html_formatter/src/trivia.rs | 41 +++ .../src/utils/formatters.rs | 6 +- .../expressions/number_literal_expression.rs | 1 - .../src/js/objects/literal_member_name.rs | 2 +- crates/biome_js_formatter/src/lib.rs | 38 ++- crates/biome_js_formatter/src/prelude.rs | 3 +- crates/biome_js_formatter/src/separated.rs | 10 +- crates/biome_js_formatter/src/trivia.rs | 78 ++++++ .../src/ts/types/number_literal_type.rs | 2 +- .../src/ts/types/union_type.rs | 2 +- crates/biome_js_formatter/src/utils/array.rs | 9 +- .../src/json/value/number_value.rs | 2 +- crates/biome_json_formatter/src/lib.rs | 56 +++- crates/biome_json_formatter/src/prelude.rs | 2 +- crates/biome_json_formatter/src/separated.rs | 7 +- crates/biome_json_formatter/src/trivia.rs | 78 ++++++ 43 files changed, 811 insertions(+), 250 deletions(-) create mode 100644 crates/biome_css_formatter/src/trivia.rs create mode 100644 crates/biome_graphql_formatter/src/trivia.rs create mode 100644 crates/biome_grit_formatter/src/trivia.rs create mode 100644 crates/biome_html_formatter/src/trivia.rs create mode 100644 crates/biome_js_formatter/src/trivia.rs create mode 100644 crates/biome_json_formatter/src/trivia.rs diff --git a/crates/biome_css_formatter/src/css/value/number.rs b/crates/biome_css_formatter/src/css/value/number.rs index 84e4d08b90e4..350cb797c4bf 100644 --- a/crates/biome_css_formatter/src/css/value/number.rs +++ b/crates/biome_css_formatter/src/css/value/number.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use biome_css_syntax::CssNumber; -use biome_formatter::token::number::{NumberFormatOptions, format_number_token}; +use biome_formatter::token::number::NumberFormatOptions; #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssNumber; diff --git a/crates/biome_css_formatter/src/css/value/regular_dimension.rs b/crates/biome_css_formatter/src/css/value/regular_dimension.rs index 751d77b2bafc..01ec0f68f789 100644 --- a/crates/biome_css_formatter/src/css/value/regular_dimension.rs +++ b/crates/biome_css_formatter/src/css/value/regular_dimension.rs @@ -1,9 +1,6 @@ use crate::{prelude::*, utils::string_utils::FormatTokenAsLowercase}; use biome_css_syntax::{CssRegularDimension, CssRegularDimensionFields}; -use biome_formatter::{ - token::number::{NumberFormatOptions, format_number_token}, - write, -}; +use biome_formatter::{token::number::NumberFormatOptions, write}; #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssRegularDimension; diff --git a/crates/biome_css_formatter/src/lib.rs b/crates/biome_css_formatter/src/lib.rs index 421234a5d093..2896ee4fa147 100644 --- a/crates/biome_css_formatter/src/lib.rs +++ b/crates/biome_css_formatter/src/lib.rs @@ -7,6 +7,7 @@ mod cst; mod generated; mod prelude; mod separated; +mod trivia; mod utils; mod verbatim; @@ -17,13 +18,14 @@ pub(crate) use crate::context::CssFormatContext; use crate::context::CssFormatOptions; use crate::cst::FormatCssSyntaxNode; use crate::prelude::{format_bogus_node, format_suppressed_node}; +pub(crate) use crate::trivia::*; use biome_css_syntax::{ AnyCssDeclarationBlock, AnyCssRule, AnyCssRuleBlock, AnyCssValue, CssLanguage, CssSyntaxKind, CssSyntaxNode, CssSyntaxToken, }; use biome_formatter::comments::Comments; use biome_formatter::prelude::*; -use biome_formatter::trivia::format_skipped_token_trivia; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{ CstFormatContext, FormatContext, FormatLanguage, FormatOwnedWithRule, FormatRefWithRule, TransformSourceMap, write, @@ -306,23 +308,33 @@ impl FormatRule for FormatCssSyntaxToken { fn fmt(&self, token: &CssSyntaxToken, f: &mut Formatter) -> FormatResult<()> { f.state_mut().track_token(token); - write!(f, [format_skipped_token_trivia(token)])?; + self.format_skipped_token_trivia(token, f)?; if token.kind().is_contextual_keyword() { let original = token.text_trimmed(); match original.to_ascii_lowercase_cow() { - Cow::Borrowed(_) => write!(f, [format_trimmed_token(token)]), + Cow::Borrowed(_) => self.format_trimmed_token_trivia(token, f), Cow::Owned(lowercase) => write!( f, [dynamic_text(&lowercase, token.text_trimmed_range().start())] ), } } else { - write!(f, [format_trimmed_token(token)]) + self.format_trimmed_token_trivia(token, f) } } } +impl FormatToken for FormatCssSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &CssSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} + impl AsFormat for CssSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatCssSyntaxToken>; diff --git a/crates/biome_css_formatter/src/prelude.rs b/crates/biome_css_formatter/src/prelude.rs index 77a2767beb4b..49a04b99f22d 100644 --- a/crates/biome_css_formatter/src/prelude.rs +++ b/crates/biome_css_formatter/src/prelude.rs @@ -5,7 +5,7 @@ pub(crate) use crate::separated::FormatAstSeparatedListExtension; pub(crate) use crate::{ AsFormat, CssFormatContext, CssFormatter, FormatNodeRule, FormattedIterExt as _, IntoFormat, - verbatim::*, + format_number_token, format_removed, format_replaced, on_removed, on_skipped, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{ diff --git a/crates/biome_css_formatter/src/separated.rs b/crates/biome_css_formatter/src/separated.rs index 621fb98c12a4..6f31990c34f9 100644 --- a/crates/biome_css_formatter/src/separated.rs +++ b/crates/biome_css_formatter/src/separated.rs @@ -30,10 +30,11 @@ where } } -type CssFormatSeparatedIter = FormatSeparatedIter< +type CssFormatSeparatedIter = FormatSeparatedIter< AstSeparatedListElementsIterator, Node, CssFormatSeparatedElementRule, + C, >; /// AST Separated list formatting extension methods @@ -46,11 +47,16 @@ pub(crate) trait FormatAstSeparatedListExtension: /// calling the `separator_factory` function. The last trailing separator /// will not be printed by default. Use `with_trailing_separator` to add it /// in where necessary. - fn format_separated(&self, separator: &'static str) -> CssFormatSeparatedIter { + fn format_separated( + &self, + separator: &'static str, + ) -> CssFormatSeparatedIter { CssFormatSeparatedIter::new( self.elements(), separator, CssFormatSeparatedElementRule { node: PhantomData }, + on_skipped, + on_removed, ) .with_trailing_separator(TrailingSeparator::Disallowed) } @@ -94,10 +100,11 @@ where } } -type CssFormatSeparatedIterWithOptions = FormatSeparatedIter< +type CssFormatSeparatedIterWithOptions = FormatSeparatedIter< AstSeparatedListElementsIterator, Node, CssFormatSeparatedElementRuleWithOptions, + C, >; /// AST Separated list formatting extension methods with options @@ -115,11 +122,13 @@ pub(crate) trait FormatAstSeparatedListWithOptionsExtension: &self, separator: &'static str, options: O, - ) -> CssFormatSeparatedIterWithOptions { + ) -> CssFormatSeparatedIterWithOptions { FormatSeparatedIter::new( self.elements(), separator, CssFormatSeparatedElementRuleWithOptions::new(options), + on_skipped, + on_removed, ) .with_trailing_separator(TrailingSeparator::Disallowed) } diff --git a/crates/biome_css_formatter/src/trivia.rs b/crates/biome_css_formatter/src/trivia.rs new file mode 100644 index 000000000000..76405aa2d41d --- /dev/null +++ b/crates/biome_css_formatter/src/trivia.rs @@ -0,0 +1,78 @@ +use crate::prelude::CssFormatContext; +use crate::{CssFormatter, FormatCssSyntaxToken}; +use biome_css_syntax::CssSyntaxToken; +use biome_formatter::formatter::Formatter; +use biome_formatter::prelude::{ + NumberFormatOptions, format_trimmed_number, syntax_token_cow_slice, +}; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Argument, Format, FormatResult}; + +pub(crate) struct FormatRemoved<'a> { + token: &'a CssSyntaxToken, +} + +pub(crate) fn format_removed(token: &CssSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatCssSyntaxToken.format_removed(self.token, f) + } +} + +pub(crate) struct FormatReplaced<'a> { + token: &'a CssSyntaxToken, + content: Argument<'a, CssFormatContext>, +} + +pub(crate) fn format_replaced<'a>( + token: &'a CssSyntaxToken, + content: &'a impl Format, +) -> FormatReplaced<'a> { + FormatReplaced { + token, + content: Argument::new(content), + } +} + +impl<'a> Format for FormatReplaced<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatCssSyntaxToken.format_replaced(self.token, &self.content, f) + } +} + +pub fn format_number_token( + token: &CssSyntaxToken, + options: NumberFormatOptions, +) -> CleanedNumberLiteralText<'_> { + CleanedNumberLiteralText { token, options } +} + +pub(crate) struct CleanedNumberLiteralText<'a> { + token: &'a CssSyntaxToken, + options: NumberFormatOptions, +} + +impl Format for CleanedNumberLiteralText<'_> { + fn fmt(&self, f: &mut CssFormatter) -> FormatResult<()> { + format_replaced( + &self.token, + &syntax_token_cow_slice( + format_trimmed_number(self.token.text_trimmed(), self.options), + &self.token, + self.token.text_trimmed_range().start(), + ), + ) + .fmt(f) + } +} + +pub(crate) fn on_skipped(token: &CssSyntaxToken, f: &mut CssFormatter) -> FormatResult<()> { + FormatCssSyntaxToken.format_skipped_token_trivia(token, f) +} + +pub(crate) fn on_removed(token: &CssSyntaxToken, f: &mut CssFormatter) -> FormatResult<()> { + FormatCssSyntaxToken.format_removed(token, f) +} diff --git a/crates/biome_css_formatter/src/utils/string_utils.rs b/crates/biome_css_formatter/src/utils/string_utils.rs index 5b8bca64914a..d3d57c9d303c 100644 --- a/crates/biome_css_formatter/src/utils/string_utils.rs +++ b/crates/biome_css_formatter/src/utils/string_utils.rs @@ -10,7 +10,6 @@ use biome_formatter::token::string::normalize_string; use biome_formatter::{ Format, FormatResult, prelude::{dynamic_text, write}, - trivia::format_replaced, }; use biome_rowan::SyntaxToken; use biome_string_case::StrLikeExtension; diff --git a/crates/biome_formatter/src/comments.rs b/crates/biome_formatter/src/comments.rs index c753f83a2195..88ae5c3282d8 100644 --- a/crates/biome_formatter/src/comments.rs +++ b/crates/biome_formatter/src/comments.rs @@ -158,19 +158,19 @@ impl CommentKind { #[derive(Debug, Clone)] pub struct SourceComment { /// The number of lines appearing before this comment - pub(crate) lines_before: u32, + pub lines_before: u32, - pub(crate) lines_after: u32, + pub lines_after: u32, /// The comment piece - pub(crate) piece: SyntaxTriviaPieceComments, + pub piece: SyntaxTriviaPieceComments, /// The kind of the comment. - pub(crate) kind: CommentKind, + pub kind: CommentKind, /// Whether the comment has been formatted or not. #[cfg(debug_assertions)] - pub(crate) formatted: Cell, + pub formatted: Cell, } impl SourceComment { diff --git a/crates/biome_formatter/src/lib.rs b/crates/biome_formatter/src/lib.rs index 0914709c5e0f..867695e3d9d5 100644 --- a/crates/biome_formatter/src/lib.rs +++ b/crates/biome_formatter/src/lib.rs @@ -54,7 +54,6 @@ use crate::format_element::document::Document; #[cfg(debug_assertions)] use crate::printed_tokens::PrintedTokens; use crate::printer::{Printer, PrinterOptions}; -use crate::trivia::{format_skipped_token_trivia, format_trimmed_token}; pub use arguments::{Argument, Arguments}; use biome_console::markup; use biome_deserialize::{ @@ -75,7 +74,6 @@ pub use builders::BestFitting; pub use format_element::{FormatElement, LINE_TERMINATORS, normalize_newlines}; pub use group_id::GroupId; pub use source_map::{TransformSourceMap, TransformSourceMapBuilder}; -use std::marker::PhantomData; use std::num::ParseIntError; use std::str::FromStr; use token::string::Quote; @@ -1131,43 +1129,6 @@ pub trait FormatRule { fn fmt(&self, item: &T, f: &mut Formatter) -> FormatResult<()>; } -/// Default implementation for formatting a token -pub struct FormatToken { - context: PhantomData, -} - -impl Default for FormatToken { - fn default() -> Self { - Self { - context: PhantomData, - } - } -} - -impl FormatRule> for FormatToken -where - C: CstFormatContext, - C::Language: 'static, -{ - type Context = C; - - fn fmt( - &self, - token: &SyntaxToken, - f: &mut Formatter, - ) -> FormatResult<()> { - f.state_mut().track_token(token); - - crate::write!( - f, - [ - format_skipped_token_trivia(token), - format_trimmed_token(token), - ] - ) - } -} - /// Rule that supports customizing how it formats an object of type `T`. pub trait FormatRuleWithOptions: FormatRule { type Options; diff --git a/crates/biome_formatter/src/prelude.rs b/crates/biome_formatter/src/prelude.rs index 22f0a6926702..43c7d7c71158 100644 --- a/crates/biome_formatter/src/prelude.rs +++ b/crates/biome_formatter/src/prelude.rs @@ -4,13 +4,14 @@ pub use crate::format_extensions::{MemoizeFormat, Memoized}; pub use crate::formatter::Formatter; pub use crate::printer::PrinterOptions; pub use crate::trivia::{ - format_dangling_comments, format_leading_comments, format_only_if_breaks, format_removed, - format_replaced, format_trailing_comments, format_trimmed_token, + format_dangling_comments, format_leading_comments, format_only_if_breaks, + format_trailing_comments, should_nestle_adjacent_doc_comments, }; pub use crate::diagnostics::FormatError; pub use crate::format_element::document::Document; pub use crate::format_element::tag::{LabelId, Tag, TagKind}; +pub use crate::token::number::{NumberFormatOptions, format_trimmed_number}; pub use crate::{ Buffer as _, BufferExtensions, Format, Format as _, FormatResult, FormatRule, diff --git a/crates/biome_formatter/src/separated.rs b/crates/biome_formatter/src/separated.rs index ede3df92b18f..2b22cb56afc0 100644 --- a/crates/biome_formatter/src/separated.rs +++ b/crates/biome_formatter/src/separated.rs @@ -23,7 +23,7 @@ where /// Formats a single element inside a separated list. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct FormatSeparatedElement +pub struct FormatSeparatedElement where N: AstNode, R: FormatSeparatedElementRule, @@ -34,9 +34,11 @@ where /// The separator to write if the element has no separator yet. separator: &'static str, options: FormatSeparatedOptions, + on_skipped: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, + on_removed: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, } -impl FormatSeparatedElement +impl FormatSeparatedElement where N: AstNode, R: FormatSeparatedElementRule, @@ -47,7 +49,7 @@ where } } -impl Format for FormatSeparatedElement +impl Format for FormatSeparatedElement where N: AstNode, N::Language: 'static, @@ -77,7 +79,7 @@ where // Use format_replaced instead of wrapping the result of format_token // in order to remove only the token itself when the group doesn't break // but still print its associated trivia unconditionally - format_only_if_breaks(separator, &format_separator) + format_only_if_breaks(separator, &format_separator, self.on_skipped) .with_group_id(self.options.group_id) .fmt(f)?; } @@ -88,9 +90,7 @@ where // A trailing separator was present where it wasn't allowed, opt out of formatting return Err(FormatError::SyntaxError); } - TrailingSeparator::Omit => { - write!(f, [format_removed(separator)])?; - } + TrailingSeparator::Omit => (self.on_removed)(separator, f)?, } } else { write!(f, [format_separator])?; @@ -121,28 +121,40 @@ where /// Iterator for formatting separated elements. Prints the separator between each element and /// inserts a trailing separator if necessary -pub struct FormatSeparatedIter +pub struct FormatSeparatedIter where Node: AstNode, + C: CstFormatContext, { next: Option>, rule: Rule, inner: I, separator: &'static str, options: FormatSeparatedOptions, + on_skipped: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, + on_removed: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, } -impl FormatSeparatedIter +impl FormatSeparatedIter where Node: AstNode, + C: CstFormatContext, { - pub fn new(inner: I, separator: &'static str, rule: Rule) -> Self { + pub fn new( + inner: I, + separator: &'static str, + rule: Rule, + on_skipped: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, + on_removed: fn(&SyntaxToken, &mut Formatter) -> FormatResult<()>, + ) -> Self { Self { inner, rule, separator, next: None, options: FormatSeparatedOptions::default(), + on_skipped, + on_removed, } } @@ -163,13 +175,14 @@ where } } -impl Iterator for FormatSeparatedIter +impl Iterator for FormatSeparatedIter where Node: AstNode, I: Iterator>, Rule: FormatSeparatedElementRule + Clone, + C: CstFormatContext, { - type Item = FormatSeparatedElement; + type Item = FormatSeparatedElement; fn next(&mut self) -> Option { let element = self.next.take().or_else(|| self.inner.next())?; @@ -183,23 +196,27 @@ where is_last, separator: self.separator, options: self.options, + on_skipped: self.on_skipped, + on_removed: self.on_removed, }) } } -impl std::iter::FusedIterator for FormatSeparatedIter +impl std::iter::FusedIterator for FormatSeparatedIter where Node: AstNode, I: Iterator> + std::iter::FusedIterator, Rule: FormatSeparatedElementRule + Clone, + C: CstFormatContext, { } -impl std::iter::ExactSizeIterator for FormatSeparatedIter +impl std::iter::ExactSizeIterator for FormatSeparatedIter where Node: AstNode, I: Iterator> + ExactSizeIterator, Rule: FormatSeparatedElementRule + Clone, + C: CstFormatContext, { } diff --git a/crates/biome_formatter/src/token/number.rs b/crates/biome_formatter/src/token/number.rs index 0a2a9bd48204..cbb8489cbfe4 100644 --- a/crates/biome_formatter/src/token/number.rs +++ b/crates/biome_formatter/src/token/number.rs @@ -1,11 +1,7 @@ -use biome_rowan::{Language, SyntaxToken}; use biome_string_case::StrLikeExtension; use std::borrow::Cow; use std::num::NonZeroUsize; -use crate::prelude::*; -use crate::{CstFormatContext, Format}; - #[derive(Debug, Default, Clone, Copy)] pub struct NumberFormatOptions { /// Controls how numbers with trailing decimal zeroes are formatted. @@ -24,42 +20,6 @@ impl NumberFormatOptions { } } -pub fn format_number_token( - token: &SyntaxToken, - options: NumberFormatOptions, -) -> CleanedNumberLiteralText -where - L: Language, -{ - CleanedNumberLiteralText { token, options } -} - -pub struct CleanedNumberLiteralText<'token, L> -where - L: Language, -{ - token: &'token SyntaxToken, - options: NumberFormatOptions, -} - -impl Format for CleanedNumberLiteralText<'_, L> -where - L: Language + 'static, - C: CstFormatContext, -{ - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - format_replaced( - self.token, - &syntax_token_cow_slice( - format_trimmed_number(self.token.text_trimmed(), self.options), - self.token, - self.token.text_trimmed_range().start(), - ), - ) - .fmt(f) - } -} - enum FormatNumberLiteralState { IntegerPart, DecimalPart(FormatNumberLiteralDecimalPart), @@ -77,7 +37,7 @@ struct FormatNumberLiteralExponent { first_non_zero_index: Option, } // Regex-free version of https://github.com/prettier/prettier/blob/ca246afacee8e6d5db508dae01730c9523bbff1d/src/common/util.js#L341-L356 -fn format_trimmed_number(text: &str, options: NumberFormatOptions) -> Cow { +pub fn format_trimmed_number(text: &str, options: NumberFormatOptions) -> Cow { use FormatNumberLiteralState::*; let text = text.to_ascii_lowercase_cow(); diff --git a/crates/biome_formatter/src/trivia.rs b/crates/biome_formatter/src/trivia.rs index d67180c5c5bd..ef9f48c7bd9d 100644 --- a/crates/biome_formatter/src/trivia.rs +++ b/crates/biome_formatter/src/trivia.rs @@ -26,7 +26,7 @@ use std::ops::Sub; /// There isn't much documentation about this behavior, but it is mentioned on the JSDoc repo /// for documentation: https://github.com/jsdoc/jsdoc.github.io/issues/40. Prettier also /// implements the same behavior: https://github.com/prettier/prettier/pull/13445/files#diff-3d5eaa2a1593372823589e6e55e7ca905f7c64203ecada0aa4b3b0cdddd5c3ddR160-R178 -fn should_nestle_adjacent_doc_comments( +pub fn should_nestle_adjacent_doc_comments( first_comment: &SourceComment, second_comment: &SourceComment, ) -> bool { @@ -356,96 +356,184 @@ where } } -/// Formats a token without its skipped token trivia -/// -/// ## Warning -/// It's your responsibility to format any skipped trivia. -pub const fn format_trimmed_token(token: &SyntaxToken) -> FormatTrimmedToken { - FormatTrimmedToken { token } -} - -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct FormatTrimmedToken<'a, L: Language> { - token: &'a SyntaxToken, -} - -impl Format for FormatTrimmedToken<'_, L> +pub trait FormatToken where + L: Language, C: CstFormatContext, { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let trimmed_range = self.token.text_trimmed_range(); - located_token_text(self.token, trimmed_range).fmt(f) + fn has_skipped(&self, token: &SyntaxToken, f: &mut Formatter) -> bool { + f.comments().has_skipped(token) } -} -/// Formats the skipped token trivia of a removed token and marks the token as tracked. -pub const fn format_removed(token: &SyntaxToken) -> FormatRemoved -where - L: Language, -{ - FormatRemoved { token } -} + fn format_removed(&self, token: &SyntaxToken, f: &mut Formatter) -> FormatResult<()> { + f.state_mut().track_token(token); -/// Formats the trivia of a token that is present in the source text but should be omitted in the -/// formatted output. -pub struct FormatRemoved<'a, L> -where - L: Language, -{ - token: &'a SyntaxToken, -} + self.format_skipped_token_trivia(token, f) + } -impl Format for FormatRemoved<'_, L> -where - L: Language + 'static, - C: CstFormatContext, -{ - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - f.state_mut().track_token(self.token); + /// Formats the skipped token trivia of `token`. + fn format_skipped_token_trivia( + &self, + token: &SyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + if f.comments().has_skipped(token) { + self.fmt_skipped(token, f) + } else { + Ok(()) + } + } - write!(f, [format_skipped_token_trivia(self.token)]) + /// Formats a token without its skipped token trivia + /// + /// ## Warning + /// It's your responsibility to format any skipped trivia. + fn format_trimmed_token_trivia( + &self, + token: &SyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + let trimmed_range = token.text_trimmed_range(); + located_token_text(token, trimmed_range).fmt(f) } -} -/// Print out a `token` from the original source with a different `content`. -/// -/// This will print the skipped token trivia that belong to `token` to `content`; -/// `token` is then marked as consumed by the formatter. -pub fn format_replaced<'a, 'content, L, Context>( - token: &'a SyntaxToken, - content: &'content impl Format, -) -> FormatReplaced<'a, 'content, L, Context> -where - L: Language, -{ - FormatReplaced { - token, - content: Argument::new(content), + /// Print out a `token` from the original source with a different `content`. + /// + /// This will print the skipped token trivia that belong to `token` to `content`; + /// `token` is then marked as consumed by the formatter. + fn format_replaced( + &self, + token: &SyntaxToken, + content: &impl Format, + f: &mut Formatter, + ) -> FormatResult<()> { + f.state_mut().track_token(token); + self.format_skipped_token_trivia(token, f)?; + f.write_fmt(Arguments::from(&Argument::new(content))) } -} -/// Formats a token's skipped token trivia but uses the provided content instead -/// of the token in the formatted output. -#[derive(Copy, Clone)] -pub struct FormatReplaced<'a, 'content, L, C> -where - L: Language, -{ - token: &'a SyntaxToken, - content: Argument<'content, C>, -} + #[cold] + fn fmt_skipped(&self, token: &SyntaxToken, f: &mut Formatter) -> FormatResult<()> { + // Lines/spaces before the next token/comment + let (mut lines, mut spaces) = match token.prev_token() { + Some(token) => { + let mut lines = 0u32; + let mut spaces = 0u32; + for piece in token.trailing_trivia().pieces().rev() { + if piece.is_whitespace() { + spaces += 1; + } else if piece.is_newline() { + spaces = 0; + lines += 1; + } else { + break; + } + } -impl Format for FormatReplaced<'_, '_, L, C> -where - L: Language + 'static, - C: CstFormatContext, -{ - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - f.state_mut().track_token(self.token); + (lines, spaces) + } + None => (0, 0), + }; + + // The comments between the last skipped token trivia and the token + let mut dangling_comments = Vec::new(); + let mut skipped_range: Option = None; - write!(f, [format_skipped_token_trivia(self.token)])?; + // Iterate over the remaining pieces to find the full range from the first to the last skipped token trivia. + // Extract the comments between the last skipped token trivia and the token. + for piece in token.leading_trivia().pieces() { + if piece.is_whitespace() { + spaces += 1; + continue; + } - f.write_fmt(Arguments::from(&self.content)) + if piece.is_newline() { + lines += 1; + spaces = 0; + } else if let Some(comment) = piece.as_comments() { + let source_comment = SourceComment { + kind: C::Style::get_comment_kind(&comment), + lines_before: lines, + lines_after: 0, + piece: comment, + #[cfg(debug_assertions)] + formatted: Cell::new(true), + }; + + dangling_comments.push(source_comment); + + lines = 0; + spaces = 0; + } else if piece.is_skipped() { + skipped_range = Some(match skipped_range { + Some(range) => range.cover(piece.text_range()), + None => { + if dangling_comments.is_empty() { + match lines { + 0 if spaces == 0 => { + // Token had no space to previous token nor any preceding comment. Keep it that way + } + 0 => write!(f, [space()])?, + _ => write!(f, [hard_line_break()])?, + }; + } else { + match lines { + 0 => write!(f, [space()])?, + 1 => write!(f, [hard_line_break()])?, + _ => write!(f, [empty_line()])?, + }; + } + + piece.text_range() + } + }); + + lines = 0; + spaces = 0; + dangling_comments.clear(); + } + } + + let skipped_range = + skipped_range.unwrap_or_else(|| TextRange::empty(token.text_range().start())); + + f.write_element(FormatElement::Tag(Tag::StartVerbatim( + VerbatimKind::Verbatim { + length: skipped_range.len(), + }, + )))?; + write!(f, [located_token_text(token, skipped_range)])?; + f.write_element(FormatElement::Tag(Tag::EndVerbatim))?; + + // Write whitespace separator between skipped/last comment and token + if dangling_comments.is_empty() { + match lines { + 0 if spaces == 0 => { + // Don't write a space if there was non in the source document + Ok(()) + } + 0 => write!(f, [space()]), + _ => write!(f, [hard_line_break()]), + } + } else { + match dangling_comments.first().unwrap().lines_before { + 0 => write!(f, [space()])?, + 1 => write!(f, [hard_line_break()])?, + _ => write!(f, [empty_line()])?, + } + + write!( + f, + [FormatDanglingComments::Comments { + comments: &dangling_comments, + indent: DanglingIndentMode::None + }] + )?; + + match lines { + 0 => write!(f, [space()]), + _ => write!(f, [hard_line_break()]), + } + } } } @@ -453,6 +541,7 @@ where pub fn format_only_if_breaks<'a, 'content, L, Content, Context>( token: &'a SyntaxToken, content: &'content Content, + ok_skipped: fn(&'a SyntaxToken, &mut Formatter) -> FormatResult<()>, ) -> FormatOnlyIfBreaks<'a, 'content, L, Context> where L: Language, @@ -462,6 +551,7 @@ where token, content: Argument::new(content), group_id: None, + ok_skipped, } } @@ -474,6 +564,7 @@ where token: &'a SyntaxToken, content: Argument<'content, C>, group_id: Option, + ok_skipped: fn(&'a SyntaxToken, &mut Formatter) -> FormatResult<()>, } impl FormatOnlyIfBreaks<'_, '_, L, C> @@ -497,14 +588,13 @@ where [if_group_breaks(&Arguments::from(&self.content)).with_group_id(self.group_id),] )?; + let skipped = format_with(|f| (self.ok_skipped)(self.token, f)); + if f.comments().has_skipped(self.token) { // Print the trivia otherwise write!( f, - [ - if_group_fits_on_line(&format_skipped_token_trivia(self.token)) - .with_group_id(self.group_id) - ] + [if_group_fits_on_line(&skipped).with_group_id(self.group_id)] )?; } diff --git a/crates/biome_graphql_formatter/src/graphql/auxiliary/union_member_types.rs b/crates/biome_graphql_formatter/src/graphql/auxiliary/union_member_types.rs index 305af92cacff..89a594ad4c77 100644 --- a/crates/biome_graphql_formatter/src/graphql/auxiliary/union_member_types.rs +++ b/crates/biome_graphql_formatter/src/graphql/auxiliary/union_member_types.rs @@ -1,4 +1,6 @@ +use crate::FormatGraphqlSyntaxToken; use crate::prelude::*; +use biome_formatter::trivia::FormatToken; use biome_formatter::{format_args, write}; use biome_graphql_syntax::{ GraphqlSyntaxToken, GraphqlUnionMemberTypes, GraphqlUnionMemberTypesFields, @@ -41,6 +43,10 @@ pub struct FormatTypeLeadingSeparator<'a> { leading_separator: Option<&'a GraphqlSyntaxToken>, } +fn on_skipped(token: &GraphqlSyntaxToken, f: &mut GraphqlFormatter) -> FormatResult<()> { + FormatGraphqlSyntaxToken.format_skipped_token_trivia(token, f) +} + impl Format for FormatTypeLeadingSeparator<'_> { fn fmt(&self, f: &mut GraphqlFormatter) -> FormatResult<()> { match &self.leading_separator { @@ -48,7 +54,7 @@ impl Format for FormatTypeLeadingSeparator<'_> { let content = format_with(|f| { write!(f, [soft_line_break_or_space(), token.format(), space()]) }); - write!(f, [format_only_if_breaks(token, &content)]) + write!(f, [format_only_if_breaks(token, &content, on_skipped)]) } None => { let content = format_with(|f| { diff --git a/crates/biome_graphql_formatter/src/graphql/definitions/directive_definition.rs b/crates/biome_graphql_formatter/src/graphql/definitions/directive_definition.rs index 6c91e388fd99..f8e674879b11 100644 --- a/crates/biome_graphql_formatter/src/graphql/definitions/directive_definition.rs +++ b/crates/biome_graphql_formatter/src/graphql/definitions/directive_definition.rs @@ -1,4 +1,6 @@ +use crate::FormatGraphqlSyntaxToken; use crate::prelude::*; +use biome_formatter::trivia::FormatToken; use biome_formatter::write; use biome_graphql_syntax::{GraphqlDirectiveDefinition, GraphqlDirectiveDefinitionFields}; @@ -27,7 +29,7 @@ impl FormatNodeRule for FormatGraphqlDirectiveDefini } if let Some(bitwise_or_token) = bitwise_or_token { - write!(f, [format_removed(&bitwise_or_token)])?; + FormatGraphqlSyntaxToken.format_removed(&bitwise_or_token, f)?; } write!( diff --git a/crates/biome_graphql_formatter/src/graphql/lists/union_member_type_list.rs b/crates/biome_graphql_formatter/src/graphql/lists/union_member_type_list.rs index 53bf885e7868..4364d302d8a3 100644 --- a/crates/biome_graphql_formatter/src/graphql/lists/union_member_type_list.rs +++ b/crates/biome_graphql_formatter/src/graphql/lists/union_member_type_list.rs @@ -1,4 +1,6 @@ +use crate::FormatGraphqlSyntaxToken; use crate::prelude::*; +use biome_formatter::trivia::FormatToken; use biome_formatter::write; use biome_graphql_syntax::{GraphqlLanguage, GraphqlNameReference, GraphqlUnionMemberTypeList}; use biome_rowan::AstSeparatedElement; @@ -37,7 +39,7 @@ impl Format for FormatTypeVariant { if let Some(token) = separator { if self.last { - write!(f, [format_removed(token)])?; + FormatGraphqlSyntaxToken.format_removed(token, f)?; } else { write![f, [soft_line_break_or_space(), token.format()]]?; } diff --git a/crates/biome_graphql_formatter/src/graphql/value/string_value.rs b/crates/biome_graphql_formatter/src/graphql/value/string_value.rs index 240e4198a821..858eb157940c 100644 --- a/crates/biome_graphql_formatter/src/graphql/value/string_value.rs +++ b/crates/biome_graphql_formatter/src/graphql/value/string_value.rs @@ -1,4 +1,6 @@ +use crate::FormatGraphqlSyntaxToken; use crate::prelude::*; +use biome_formatter::trivia::FormatToken; use biome_formatter::write; use biome_graphql_syntax::{GraphqlStringValue, GraphqlStringValueFields, TextLen}; @@ -65,7 +67,7 @@ impl FormatNodeRule for FormatGraphqlStringValue { join.finish() }); - write!(f, [format_replaced(&token, &content)]) + FormatGraphqlSyntaxToken.format_replaced(&token, &content, f) } else { write![f, [graphql_string_literal_token.format()]] } diff --git a/crates/biome_graphql_formatter/src/lib.rs b/crates/biome_graphql_formatter/src/lib.rs index 2f3e2bae3f11..2c5cddf99571 100644 --- a/crates/biome_graphql_formatter/src/lib.rs +++ b/crates/biome_graphql_formatter/src/lib.rs @@ -6,6 +6,7 @@ mod cst; mod generated; mod graphql; mod prelude; +mod trivia; mod utils; mod verbatim; @@ -14,15 +15,17 @@ pub(crate) use crate::context::GraphqlFormatContext; use crate::context::GraphqlFormatOptions; use crate::cst::FormatGraphqlSyntaxNode; use crate::prelude::{format_bogus_node, format_suppressed_node}; +pub(crate) use crate::trivia::*; use biome_formatter::comments::Comments; use biome_formatter::prelude::*; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{ CstFormatContext, FormatContext, FormatLanguage, FormatOwnedWithRule, FormatRefWithRule, - FormatToken, TransformSourceMap, write, + TransformSourceMap, write, }; use biome_formatter::{Formatted, Printed}; use biome_graphql_syntax::{GraphqlLanguage, GraphqlSyntaxNode, GraphqlSyntaxToken}; -use biome_rowan::{AstNode, SyntaxNode, TextRange}; +use biome_rowan::{AstNode, SyntaxNode, SyntaxToken, TextRange}; /// Used to get an object that knows how to format this object. pub(crate) trait AsFormat { @@ -237,13 +240,41 @@ where } /// Format implementation specific to Graphql tokens. -pub(crate) type FormatGraphqlSyntaxToken = FormatToken; +#[derive(Debug, Default)] +pub(crate) struct FormatGraphqlSyntaxToken; + +impl FormatRule> for FormatGraphqlSyntaxToken { + type Context = GraphqlFormatContext; + + fn fmt( + &self, + token: &GraphqlSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + f.state_mut().track_token(token); + + self.format_skipped_token_trivia(token, f)?; + self.format_trimmed_token_trivia(token, f)?; + + Ok(()) + } +} + +impl FormatToken for FormatGraphqlSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &GraphqlSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} impl AsFormat for GraphqlSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatGraphqlSyntaxToken>; fn format(&self) -> Self::Format<'_> { - FormatRefWithRule::new(self, FormatGraphqlSyntaxToken::default()) + FormatRefWithRule::new(self, FormatGraphqlSyntaxToken) } } @@ -251,7 +282,7 @@ impl IntoFormat for GraphqlSyntaxToken { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { - FormatOwnedWithRule::new(self, FormatGraphqlSyntaxToken::default()) + FormatOwnedWithRule::new(self, FormatGraphqlSyntaxToken) } } diff --git a/crates/biome_graphql_formatter/src/prelude.rs b/crates/biome_graphql_formatter/src/prelude.rs index efc2e8b6a58c..a02ae677d456 100644 --- a/crates/biome_graphql_formatter/src/prelude.rs +++ b/crates/biome_graphql_formatter/src/prelude.rs @@ -4,7 +4,7 @@ pub(crate) use crate::{ AsFormat, FormatNodeRule, FormattedIterExt as _, GraphqlFormatContext, GraphqlFormatter, - IntoFormat, verbatim::*, + IntoFormat, format_removed, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{ diff --git a/crates/biome_graphql_formatter/src/trivia.rs b/crates/biome_graphql_formatter/src/trivia.rs new file mode 100644 index 000000000000..ad2f8a0692ee --- /dev/null +++ b/crates/biome_graphql_formatter/src/trivia.rs @@ -0,0 +1,20 @@ +use crate::FormatGraphqlSyntaxToken; +use crate::prelude::GraphqlFormatContext; +use biome_formatter::formatter::Formatter; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Format, FormatResult}; +use biome_graphql_syntax::GraphqlSyntaxToken; + +pub(crate) struct FormatRemoved<'a> { + token: &'a GraphqlSyntaxToken, +} + +pub(crate) fn format_removed(token: &GraphqlSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatGraphqlSyntaxToken.format_removed(self.token, f) + } +} diff --git a/crates/biome_grit_formatter/src/cst.rs b/crates/biome_grit_formatter/src/cst.rs index e96e016543cb..8476cd1867c7 100644 --- a/crates/biome_grit_formatter/src/cst.rs +++ b/crates/biome_grit_formatter/src/cst.rs @@ -1,6 +1,8 @@ use crate::prelude::*; -use biome_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatResult, FormatToken}; -use biome_grit_syntax::{GritSyntaxNode, GritSyntaxToken, map_syntax_node}; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; +use biome_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatResult}; +use biome_grit_syntax::{GritLanguage, GritSyntaxNode, GritSyntaxToken, map_syntax_node}; +use biome_rowan::SyntaxToken; #[derive(Debug, Copy, Clone, Default)] pub struct FormatGritSyntaxNode; @@ -30,13 +32,37 @@ impl IntoFormat for GritSyntaxNode { } /// Format implementation specific to GritQL tokens. -pub(crate) type FormatGritSyntaxToken = FormatToken; +#[derive(Debug, Default)] +pub(crate) struct FormatGritSyntaxToken; + +impl FormatRule for FormatGritSyntaxToken { + type Context = GritFormatContext; + + fn fmt(&self, token: &GritSyntaxToken, f: &mut Formatter) -> FormatResult<()> { + f.state_mut().track_token(token); + + self.format_skipped_token_trivia(token, f)?; + self.format_trimmed_token_trivia(token, f)?; + + Ok(()) + } +} + +impl FormatToken for FormatGritSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &SyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} impl AsFormat for GritSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatGritSyntaxToken>; fn format(&self) -> Self::Format<'_> { - FormatRefWithRule::new(self, FormatGritSyntaxToken::default()) + FormatRefWithRule::new(self, FormatGritSyntaxToken) } } @@ -44,6 +70,6 @@ impl IntoFormat for GritSyntaxToken { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { - FormatOwnedWithRule::new(self, FormatGritSyntaxToken::default()) + FormatOwnedWithRule::new(self, FormatGritSyntaxToken) } } diff --git a/crates/biome_grit_formatter/src/lib.rs b/crates/biome_grit_formatter/src/lib.rs index 5221001a3dfb..19769799c81e 100644 --- a/crates/biome_grit_formatter/src/lib.rs +++ b/crates/biome_grit_formatter/src/lib.rs @@ -7,8 +7,11 @@ mod generated; mod grit; mod prelude; pub(crate) mod separated; +mod trivia; mod verbatim; +pub(crate) use crate::context::GritFormatContext; +pub(crate) use crate::trivia::*; use biome_formatter::{ CstFormatContext, Format, FormatLanguage, FormatResult, Formatted, Printed, comments::Comments, @@ -19,8 +22,6 @@ use biome_formatter::{ use biome_grit_syntax::{GritLanguage, GritSyntaxNode}; use comments::GritCommentStyle; -pub(crate) use crate::context::GritFormatContext; - use crate::prelude::*; use crate::verbatim::format_suppressed_node; use biome_rowan::{AstNode, TextRange}; diff --git a/crates/biome_grit_formatter/src/prelude.rs b/crates/biome_grit_formatter/src/prelude.rs index d19ebbf6d271..1d175c2a8640 100644 --- a/crates/biome_grit_formatter/src/prelude.rs +++ b/crates/biome_grit_formatter/src/prelude.rs @@ -4,7 +4,7 @@ pub(crate) use crate::{ AsFormat, FormatNodeRule, FormattedIterExt as _, FormattedIterExt, GritFormatContext, - GritFormatter, IntoFormat, verbatim::*, + GritFormatter, IntoFormat, format_removed, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{AstNode as _, AstSeparatedList}; diff --git a/crates/biome_grit_formatter/src/separated.rs b/crates/biome_grit_formatter/src/separated.rs index 232d1dcc4f61..afbfcdd82085 100644 --- a/crates/biome_grit_formatter/src/separated.rs +++ b/crates/biome_grit_formatter/src/separated.rs @@ -4,11 +4,19 @@ use biome_formatter::{ }; use crate::prelude::*; +use crate::{AsFormat, GritFormatContext, cst::FormatGritSyntaxToken}; +use biome_formatter::trivia::FormatToken; use biome_grit_syntax::{GritLanguage, GritSyntaxToken}; use biome_rowan::{AstNode, AstSeparatedListElementsIterator}; use std::marker::PhantomData; -use crate::{AsFormat, GritFormatContext, cst::FormatGritSyntaxToken}; +fn on_skipped(token: &GritSyntaxToken, f: &mut GritFormatter) -> FormatResult<()> { + FormatGritSyntaxToken.format_skipped_token_trivia(token, f) +} + +fn on_removed(token: &GritSyntaxToken, f: &mut GritFormatter) -> FormatResult<()> { + FormatGritSyntaxToken.format_removed(token, f) +} #[derive(Clone)] pub(crate) struct GritFormatSeparatedElementRule @@ -35,10 +43,11 @@ where } } -type GritFormatSeparatedIter = FormatSeparatedIter< +type GritFormatSeparatedIter = FormatSeparatedIter< AstSeparatedListElementsIterator, Node, GritFormatSeparatedElementRule, + C, >; /// AST Separated list formatting extension methods @@ -51,11 +60,16 @@ pub(crate) trait FormatAstSeparatedListExtension: /// created by calling the `separator_factory` function. /// The last trailing separator in the list will only be printed /// if the outer group breaks. - fn format_separated(&self, separator: &'static str) -> GritFormatSeparatedIter { + fn format_separated( + &self, + separator: &'static str, + ) -> GritFormatSeparatedIter { GritFormatSeparatedIter::new( self.elements(), separator, GritFormatSeparatedElementRule { node: PhantomData }, + on_skipped, + on_removed, ) } } diff --git a/crates/biome_grit_formatter/src/trivia.rs b/crates/biome_grit_formatter/src/trivia.rs new file mode 100644 index 000000000000..1ad304243c86 --- /dev/null +++ b/crates/biome_grit_formatter/src/trivia.rs @@ -0,0 +1,20 @@ +use crate::context::GritFormatContext; +use crate::cst::FormatGritSyntaxToken; +use biome_formatter::formatter::Formatter; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Format, FormatResult}; +use biome_grit_syntax::GritSyntaxToken; + +pub(crate) struct FormatRemoved<'a> { + token: &'a GritSyntaxToken, +} + +pub(crate) fn format_removed(token: &GritSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatGritSyntaxToken.format_removed(self.token, f) + } +} diff --git a/crates/biome_html_formatter/src/lib.rs b/crates/biome_html_formatter/src/lib.rs index 5f5125d7d7c8..a2261235770c 100644 --- a/crates/biome_html_formatter/src/lib.rs +++ b/crates/biome_html_formatter/src/lib.rs @@ -1,11 +1,13 @@ #![deny(clippy::use_self)] use crate::prelude::{format_bogus_node, format_suppressed_node}; +pub(crate) use crate::trivia::*; use biome_formatter::comments::Comments; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{CstFormatContext, FormatOwnedWithRule, FormatRefWithRule, prelude::*}; -use biome_formatter::{FormatLanguage, FormatResult, FormatToken, Formatted, write}; +use biome_formatter::{FormatLanguage, FormatResult, Formatted, write}; use biome_html_syntax::{HtmlLanguage, HtmlSyntaxNode, HtmlSyntaxToken}; -use biome_rowan::AstNode; +use biome_rowan::{AstNode, SyntaxToken}; use comments::HtmlCommentStyle; use context::HtmlFormatContext; pub use context::HtmlFormatOptions; @@ -19,6 +21,7 @@ mod generated; mod html; pub(crate) mod prelude; mod svelte; +mod trivia; pub mod utils; mod verbatim; @@ -159,7 +162,32 @@ impl FormatLanguage for HtmlFormatLanguage { } pub(crate) type HtmlFormatter<'buf> = Formatter<'buf, HtmlFormatContext>; -pub(crate) type FormatHtmlSyntaxToken = FormatToken; + +#[derive(Debug, Default)] +pub(crate) struct FormatHtmlSyntaxToken; + +impl FormatRule> for FormatHtmlSyntaxToken { + type Context = HtmlFormatContext; + + fn fmt(&self, token: &HtmlSyntaxToken, f: &mut Formatter) -> FormatResult<()> { + f.state_mut().track_token(token); + + self.format_skipped_token_trivia(token, f)?; + self.format_trimmed_token_trivia(token, f)?; + + Ok(()) + } +} + +impl FormatToken for FormatHtmlSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &HtmlSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} // Rule for formatting a Html [AstNode]. pub(crate) trait FormatNodeRule @@ -235,7 +263,7 @@ impl AsFormat for HtmlSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatHtmlSyntaxToken>; fn format(&self) -> Self::Format<'_> { - FormatRefWithRule::new(self, FormatHtmlSyntaxToken::default()) + FormatRefWithRule::new(self, FormatHtmlSyntaxToken) } } @@ -243,7 +271,7 @@ impl IntoFormat for HtmlSyntaxToken { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { - FormatOwnedWithRule::new(self, FormatHtmlSyntaxToken::default()) + FormatOwnedWithRule::new(self, FormatHtmlSyntaxToken) } } diff --git a/crates/biome_html_formatter/src/prelude.rs b/crates/biome_html_formatter/src/prelude.rs index 0cd89daacca9..769faa1d7d70 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, verbatim::*, + HtmlFormatter, format_removed, format_replaced, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{AstNode, AstNodeList}; diff --git a/crates/biome_html_formatter/src/trivia.rs b/crates/biome_html_formatter/src/trivia.rs new file mode 100644 index 000000000000..ceb4b9213761 --- /dev/null +++ b/crates/biome_html_formatter/src/trivia.rs @@ -0,0 +1,41 @@ +use crate::FormatHtmlSyntaxToken; +use crate::prelude::HtmlFormatContext; +use biome_formatter::formatter::Formatter; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Argument, Format, FormatResult}; +use biome_html_syntax::HtmlSyntaxToken; + +pub(crate) struct FormatRemoved<'a> { + token: &'a HtmlSyntaxToken, +} + +pub(crate) fn format_removed(token: &HtmlSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatHtmlSyntaxToken.format_removed(self.token, f) + } +} + +pub(crate) struct FormatReplaced<'a> { + token: &'a HtmlSyntaxToken, + content: Argument<'a, HtmlFormatContext>, +} + +pub(crate) fn format_replaced<'a>( + token: &'a HtmlSyntaxToken, + content: &'a impl Format, +) -> FormatReplaced<'a> { + FormatReplaced { + token, + content: Argument::new(content), + } +} + +impl<'a> Format for FormatReplaced<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatHtmlSyntaxToken.format_replaced(self.token, &self.content, f) + } +} diff --git a/crates/biome_html_formatter/src/utils/formatters.rs b/crates/biome_html_formatter/src/utils/formatters.rs index 7f5a5e71b7a5..c516cb423a72 100644 --- a/crates/biome_html_formatter/src/utils/formatters.rs +++ b/crates/biome_html_formatter/src/utils/formatters.rs @@ -1,13 +1,11 @@ -use std::borrow::Cow; - use crate::prelude::*; +use crate::{HtmlFormatter, context::HtmlFormatContext}; use biome_formatter::write; use biome_formatter::{Format, FormatResult}; use biome_html_syntax::HtmlLanguage; use biome_rowan::{Language, SyntaxToken}; use biome_string_case::StrLikeExtension; - -use crate::{HtmlFormatter, context::HtmlFormatContext}; +use std::borrow::Cow; // TODO: deduplicate with CSS formatter's version of this, move to `biome_formatter`. pub(crate) struct FormatTokenAsLowercase { diff --git a/crates/biome_js_formatter/src/js/expressions/number_literal_expression.rs b/crates/biome_js_formatter/src/js/expressions/number_literal_expression.rs index 43d985403d85..edc90e36f8c8 100644 --- a/crates/biome_js_formatter/src/js/expressions/number_literal_expression.rs +++ b/crates/biome_js_formatter/src/js/expressions/number_literal_expression.rs @@ -1,7 +1,6 @@ use crate::prelude::*; use biome_formatter::token::number::NumberFormatOptions; -use biome_formatter::token::number::format_number_token; use biome_js_syntax::JsNumberLiteralExpression; use biome_js_syntax::JsNumberLiteralExpressionFields; use biome_js_syntax::parentheses::NeedsParentheses; diff --git a/crates/biome_js_formatter/src/js/objects/literal_member_name.rs b/crates/biome_js_formatter/src/js/objects/literal_member_name.rs index 3f3623e77f1c..65eddfec3961 100644 --- a/crates/biome_js_formatter/src/js/objects/literal_member_name.rs +++ b/crates/biome_js_formatter/src/js/objects/literal_member_name.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::utils::{FormatLiteralStringToken, StringLiteralParentKind}; -use biome_formatter::token::number::{NumberFormatOptions, format_number_token}; +use biome_formatter::token::number::NumberFormatOptions; use biome_formatter::write; use biome_js_syntax::JsLiteralMemberNameFields; use biome_js_syntax::{JsLiteralMemberName, JsSyntaxKind}; diff --git a/crates/biome_js_formatter/src/lib.rs b/crates/biome_js_formatter/src/lib.rs index dd2d4a273404..a7a92bfabf73 100644 --- a/crates/biome_js_formatter/src/lib.rs +++ b/crates/biome_js_formatter/src/lib.rs @@ -179,14 +179,15 @@ pub mod context; mod parentheses; pub(crate) mod separated; mod syntax_rewriter; +pub(crate) mod trivia; mod verbatim; use biome_formatter::format_element::tag::Label; use biome_formatter::prelude::*; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{Buffer, FormatOwnedWithRule, FormatRefWithRule, Formatted, Printed}; use biome_formatter::{ - CstFormatContext, Format, FormatLanguage, FormatToken, TransformSourceMap, comments::Comments, - write, + CstFormatContext, Format, FormatLanguage, TransformSourceMap, comments::Comments, write, }; use biome_js_syntax::{ AnyJsDeclaration, AnyJsStatement, JsLanguage, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, @@ -198,7 +199,8 @@ use crate::comments::JsCommentStyle; use crate::context::{JsFormatContext, JsFormatOptions}; use crate::cst::FormatJsSyntaxNode; use crate::syntax_rewriter::transform; -use crate::verbatim::{format_bogus_node, format_suppressed_node}; +use crate::trivia::*; +use crate::verbatim::{format_bogus_node, format_or_verbatim, format_suppressed_node}; /// Used to get an object that knows how to format this object. pub(crate) trait AsFormat { @@ -437,13 +439,37 @@ where } /// Format implementation specific to JavaScript tokens. -pub(crate) type FormatJsSyntaxToken = FormatToken; +#[derive(Debug, Default)] +pub(crate) struct FormatJsSyntaxToken; + +impl FormatRule for FormatJsSyntaxToken { + type Context = JsFormatContext; + + fn fmt(&self, token: &JsSyntaxToken, f: &mut Formatter) -> FormatResult<()> { + f.state_mut().track_token(token); + + self.format_skipped_token_trivia(token, f)?; + self.format_trimmed_token_trivia(token, f)?; + + Ok(()) + } +} + +impl FormatToken for FormatJsSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &JsSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} impl AsFormat for JsSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatJsSyntaxToken>; fn format(&self) -> Self::Format<'_> { - FormatRefWithRule::new(self, FormatJsSyntaxToken::default()) + FormatRefWithRule::new(self, FormatJsSyntaxToken) } } @@ -451,7 +477,7 @@ impl IntoFormat for JsSyntaxToken { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { - FormatOwnedWithRule::new(self, FormatJsSyntaxToken::default()) + FormatOwnedWithRule::new(self, FormatJsSyntaxToken) } } diff --git a/crates/biome_js_formatter/src/prelude.rs b/crates/biome_js_formatter/src/prelude.rs index 556da4308688..5dca92251bd1 100644 --- a/crates/biome_js_formatter/src/prelude.rs +++ b/crates/biome_js_formatter/src/prelude.rs @@ -3,7 +3,8 @@ pub(crate) use crate::{ AsFormat as _, FormatNodeRule, FormattedIterExt, JsFormatContext, JsFormatter, - comments::JsComments, verbatim::*, + comments::JsComments, format_number_token, format_or_verbatim, format_removed, format_replaced, + on_removed, on_skipped, }; pub use biome_formatter::prelude::*; pub use biome_formatter::separated::TrailingSeparator; diff --git a/crates/biome_js_formatter/src/separated.rs b/crates/biome_js_formatter/src/separated.rs index 7838c4254bd0..f61a33daf72b 100644 --- a/crates/biome_js_formatter/src/separated.rs +++ b/crates/biome_js_formatter/src/separated.rs @@ -31,10 +31,11 @@ where } } -type JsFormatSeparatedIter = FormatSeparatedIter< +type JsFormatSeparatedIter = FormatSeparatedIter< AstSeparatedListElementsIterator, Node, JsFormatSeparatedElementRule, + C, >; /// AST Separated list formatting extension methods @@ -47,11 +48,16 @@ pub(crate) trait FormatAstSeparatedListExtension: /// created by calling the `separator_factory` function. /// The last trailing separator in the list will only be printed /// if the outer group breaks. - fn format_separated(&self, separator: &'static str) -> JsFormatSeparatedIter { + fn format_separated( + &self, + separator: &'static str, + ) -> JsFormatSeparatedIter { JsFormatSeparatedIter::new( self.elements(), separator, JsFormatSeparatedElementRule { node: PhantomData }, + on_skipped, + on_removed, ) } } diff --git a/crates/biome_js_formatter/src/trivia.rs b/crates/biome_js_formatter/src/trivia.rs new file mode 100644 index 000000000000..96f5e13700b1 --- /dev/null +++ b/crates/biome_js_formatter/src/trivia.rs @@ -0,0 +1,78 @@ +use crate::prelude::JsFormatContext; +use crate::{FormatJsSyntaxToken, JsFormatter}; +use biome_formatter::formatter::Formatter; +use biome_formatter::prelude::{ + NumberFormatOptions, format_trimmed_number, syntax_token_cow_slice, +}; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Argument, Format, FormatResult}; +use biome_js_syntax::JsSyntaxToken; + +pub(crate) struct FormatRemoved<'a> { + token: &'a JsSyntaxToken, +} + +pub(crate) fn format_removed(token: &JsSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatJsSyntaxToken.format_removed(self.token, f) + } +} + +pub(crate) struct FormatReplaced<'a> { + token: &'a JsSyntaxToken, + content: Argument<'a, JsFormatContext>, +} + +pub(crate) fn format_replaced<'a>( + token: &'a JsSyntaxToken, + content: &'a impl Format, +) -> FormatReplaced<'a> { + FormatReplaced { + token, + content: Argument::new(content), + } +} + +impl<'a> Format for FormatReplaced<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatJsSyntaxToken.format_replaced(self.token, &self.content, f) + } +} + +pub fn format_number_token( + token: &JsSyntaxToken, + options: NumberFormatOptions, +) -> CleanedNumberLiteralText<'_> { + CleanedNumberLiteralText { token, options } +} + +pub(crate) struct CleanedNumberLiteralText<'a> { + token: &'a JsSyntaxToken, + options: NumberFormatOptions, +} + +impl Format for CleanedNumberLiteralText<'_> { + fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> { + format_replaced( + &self.token, + &syntax_token_cow_slice( + format_trimmed_number(self.token.text_trimmed(), self.options), + &self.token, + self.token.text_trimmed_range().start(), + ), + ) + .fmt(f) + } +} + +pub(crate) fn on_skipped(token: &JsSyntaxToken, f: &mut JsFormatter) -> FormatResult<()> { + FormatJsSyntaxToken.format_skipped_token_trivia(token, f) +} + +pub(crate) fn on_removed(token: &JsSyntaxToken, f: &mut JsFormatter) -> FormatResult<()> { + FormatJsSyntaxToken.format_removed(token, f) +} diff --git a/crates/biome_js_formatter/src/ts/types/number_literal_type.rs b/crates/biome_js_formatter/src/ts/types/number_literal_type.rs index 2d116dd30706..929c5ebbb452 100644 --- a/crates/biome_js_formatter/src/ts/types/number_literal_type.rs +++ b/crates/biome_js_formatter/src/ts/types/number_literal_type.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use biome_formatter::token::number::{NumberFormatOptions, format_number_token}; +use biome_formatter::token::number::NumberFormatOptions; use biome_formatter::write; use biome_js_syntax::{TsNumberLiteralType, TsNumberLiteralTypeFields}; diff --git a/crates/biome_js_formatter/src/ts/types/union_type.rs b/crates/biome_js_formatter/src/ts/types/union_type.rs index 5e7b634448b2..e09238cc4e03 100644 --- a/crates/biome_js_formatter/src/ts/types/union_type.rs +++ b/crates/biome_js_formatter/src/ts/types/union_type.rs @@ -169,7 +169,7 @@ impl Format for FormatTypeSetLeadingSeparator<'_> { } write!(f, [token.format(), space()]) }); - format_only_if_breaks(token, &content).fmt(f) + format_only_if_breaks(token, &content, on_skipped).fmt(f) } None => { let content = format_with(|f| { diff --git a/crates/biome_js_formatter/src/utils/array.rs b/crates/biome_js_formatter/src/utils/array.rs index c894cebc5142..f3c2eba98daf 100644 --- a/crates/biome_js_formatter/src/utils/array.rs +++ b/crates/biome_js_formatter/src/utils/array.rs @@ -53,7 +53,14 @@ where write!(f, [format_removed(separator)])?; } _ => { - write!(f, [format_only_if_breaks(separator, &separator.format())])?; + write!( + f, + [format_only_if_breaks( + separator, + &separator.format(), + on_skipped + )] + )?; } } } else { diff --git a/crates/biome_json_formatter/src/json/value/number_value.rs b/crates/biome_json_formatter/src/json/value/number_value.rs index 63e57a4e636e..0740e24a53c3 100644 --- a/crates/biome_json_formatter/src/json/value/number_value.rs +++ b/crates/biome_json_formatter/src/json/value/number_value.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use biome_formatter::token::number::{NumberFormatOptions, format_number_token}; +use biome_formatter::token::number::NumberFormatOptions; use biome_json_syntax::JsonNumberValue; #[derive(Debug, Clone, Default)] diff --git a/crates/biome_json_formatter/src/lib.rs b/crates/biome_json_formatter/src/lib.rs index bf44e563ee0c..d060bffffc5f 100644 --- a/crates/biome_json_formatter/src/lib.rs +++ b/crates/biome_json_formatter/src/lib.rs @@ -7,18 +7,21 @@ mod generated; mod json; mod prelude; mod separated; +mod trivia; mod verbatim; use crate::comments::JsonCommentStyle; pub(crate) use crate::context::JsonFormatContext; use crate::context::JsonFormatOptions; use crate::cst::FormatJsonSyntaxNode; +pub(crate) use crate::trivia::*; use crate::verbatim::{format_bogus_node, format_suppressed_node}; use biome_formatter::comments::Comments; use biome_formatter::prelude::*; +use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia}; use biome_formatter::{ CstFormatContext, FormatContext, FormatLanguage, FormatOwnedWithRule, FormatRefWithRule, - FormatToken, TransformSourceMap, write, + TransformSourceMap, write, }; use biome_formatter::{Formatted, Printed}; use biome_json_syntax::{AnyJsonValue, JsonLanguage, JsonSyntaxNode, JsonSyntaxToken}; @@ -273,13 +276,58 @@ impl FormatLanguage for JsonFormatLanguage { } /// Format implementation specific to JSON tokens. -pub(crate) type FormatJsonSyntaxToken = FormatToken; + +#[derive(Debug, Default)] +pub(crate) struct FormatJsonSyntaxToken; + +impl FormatRule for FormatJsonSyntaxToken { + type Context = JsonFormatContext; + + fn fmt(&self, token: &JsonSyntaxToken, f: &mut Formatter) -> FormatResult<()> { + f.state_mut().track_token(token); + + self.format_skipped_token_trivia(token, f)?; + self.format_trimmed_token_trivia(token, f)?; + + Ok(()) + } +} + +impl FormatToken for FormatJsonSyntaxToken { + fn format_skipped_token_trivia( + &self, + token: &JsonSyntaxToken, + f: &mut Formatter, + ) -> FormatResult<()> { + format_skipped_token_trivia(token).fmt(f) + } +} + +// +// impl FormatToken for FormatJsonSyntaxToken { +// type Context = JsonFormatContext; +// fn format_skipped_token_trivia( +// &self, +// f: &mut Formatter, +// token: &JsonSyntaxToken, +// ) -> FormatResult<()> { +// format_skipped_token_trivia(token).fmt(f) +// } +// +// fn format_trimmed_token( +// &self, +// f: &mut Formatter, +// token: &JsonSyntaxToken, +// ) -> FormatResult<()> { +// format_trimmed_token(token).fmt(f) +// } +// } impl AsFormat for JsonSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatJsonSyntaxToken>; fn format(&self) -> Self::Format<'_> { - FormatRefWithRule::new(self, FormatJsonSyntaxToken::default()) + FormatRefWithRule::new(self, FormatJsonSyntaxToken) } } @@ -287,7 +335,7 @@ impl IntoFormat for JsonSyntaxToken { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { - FormatOwnedWithRule::new(self, FormatJsonSyntaxToken::default()) + FormatOwnedWithRule::new(self, FormatJsonSyntaxToken) } } diff --git a/crates/biome_json_formatter/src/prelude.rs b/crates/biome_json_formatter/src/prelude.rs index b6caded11474..375bc1e347ce 100644 --- a/crates/biome_json_formatter/src/prelude.rs +++ b/crates/biome_json_formatter/src/prelude.rs @@ -4,7 +4,7 @@ #![allow(unused_imports)] pub(crate) use crate::{ AsFormat, FormatNodeRule, FormattedIterExt as _, IntoFormat, JsonFormatContext, JsonFormatter, - verbatim::*, + format_number_token, format_removed, format_replaced, on_removed, on_skipped, verbatim::*, }; pub(crate) use biome_formatter::prelude::*; pub(crate) use biome_rowan::{AstNode as _, AstNodeList as _, AstSeparatedList as _}; diff --git a/crates/biome_json_formatter/src/separated.rs b/crates/biome_json_formatter/src/separated.rs index 39e21bc5079a..84a9d4501f92 100644 --- a/crates/biome_json_formatter/src/separated.rs +++ b/crates/biome_json_formatter/src/separated.rs @@ -30,10 +30,11 @@ where } } -type JsonFormatSeparatedIter = FormatSeparatedIter< +type JsonFormatSeparatedIter = FormatSeparatedIter< AstSeparatedListElementsIterator, Node, JsonFormatSeparatedElementRule, + C, >; /// AST Separated list formatting extension methods @@ -50,11 +51,13 @@ pub(crate) trait FormatAstSeparatedListExtension: &self, separator: &'static str, trailing_separator: TrailingSeparator, - ) -> JsonFormatSeparatedIter { + ) -> JsonFormatSeparatedIter { JsonFormatSeparatedIter::new( self.elements(), separator, JsonFormatSeparatedElementRule { node: PhantomData }, + on_skipped, + on_removed, ) .with_trailing_separator(trailing_separator) } diff --git a/crates/biome_json_formatter/src/trivia.rs b/crates/biome_json_formatter/src/trivia.rs new file mode 100644 index 000000000000..e0f4523bea63 --- /dev/null +++ b/crates/biome_json_formatter/src/trivia.rs @@ -0,0 +1,78 @@ +use crate::prelude::JsonFormatContext; +use crate::{FormatJsonSyntaxToken, JsonFormatter}; +use biome_formatter::formatter::Formatter; +use biome_formatter::prelude::{ + NumberFormatOptions, format_trimmed_number, syntax_token_cow_slice, +}; +use biome_formatter::trivia::FormatToken; +use biome_formatter::{Argument, Format, FormatResult}; +use biome_json_syntax::JsonSyntaxToken; + +pub(crate) struct FormatRemoved<'a> { + token: &'a JsonSyntaxToken, +} + +pub(crate) fn format_removed(token: &JsonSyntaxToken) -> FormatRemoved { + FormatRemoved { token } +} + +impl<'a> Format for FormatRemoved<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatJsonSyntaxToken.format_removed(self.token, f) + } +} + +pub(crate) struct FormatReplaced<'a> { + token: &'a JsonSyntaxToken, + content: Argument<'a, JsonFormatContext>, +} + +pub(crate) fn format_replaced<'a>( + token: &'a JsonSyntaxToken, + content: &'a impl Format, +) -> FormatReplaced<'a> { + FormatReplaced { + token, + content: Argument::new(content), + } +} + +impl<'a> Format for FormatReplaced<'a> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + FormatJsonSyntaxToken.format_replaced(self.token, &self.content, f) + } +} + +pub fn format_number_token( + token: &JsonSyntaxToken, + options: NumberFormatOptions, +) -> CleanedNumberLiteralText<'_> { + CleanedNumberLiteralText { token, options } +} + +pub(crate) struct CleanedNumberLiteralText<'a> { + token: &'a JsonSyntaxToken, + options: NumberFormatOptions, +} + +impl Format for CleanedNumberLiteralText<'_> { + fn fmt(&self, f: &mut JsonFormatter) -> FormatResult<()> { + format_replaced( + &self.token, + &syntax_token_cow_slice( + format_trimmed_number(self.token.text_trimmed(), self.options), + &self.token, + self.token.text_trimmed_range().start(), + ), + ) + .fmt(f) + } +} + +pub(crate) fn on_skipped(token: &JsonSyntaxToken, f: &mut JsonFormatter) -> FormatResult<()> { + FormatJsonSyntaxToken.format_skipped_token_trivia(token, f) +} + +pub(crate) fn on_removed(token: &JsonSyntaxToken, f: &mut JsonFormatter) -> FormatResult<()> { + FormatJsonSyntaxToken.format_removed(token, f) +} From a94c1140057aa1fd7e75182969a667cca0e9f745 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 28 Jul 2025 19:11:28 +0100 Subject: [PATCH 2/2] feedback and linting --- crates/biome_css_formatter/src/trivia.rs | 4 ++-- crates/biome_js_formatter/src/trivia.rs | 4 ++-- crates/biome_json_formatter/src/lib.rs | 20 -------------------- crates/biome_json_formatter/src/trivia.rs | 4 ++-- 4 files changed, 6 insertions(+), 26 deletions(-) diff --git a/crates/biome_css_formatter/src/trivia.rs b/crates/biome_css_formatter/src/trivia.rs index 76405aa2d41d..5196e8901b74 100644 --- a/crates/biome_css_formatter/src/trivia.rs +++ b/crates/biome_css_formatter/src/trivia.rs @@ -58,10 +58,10 @@ pub(crate) struct CleanedNumberLiteralText<'a> { impl Format for CleanedNumberLiteralText<'_> { fn fmt(&self, f: &mut CssFormatter) -> FormatResult<()> { format_replaced( - &self.token, + self.token, &syntax_token_cow_slice( format_trimmed_number(self.token.text_trimmed(), self.options), - &self.token, + self.token, self.token.text_trimmed_range().start(), ), ) diff --git a/crates/biome_js_formatter/src/trivia.rs b/crates/biome_js_formatter/src/trivia.rs index 96f5e13700b1..2096ddcb2527 100644 --- a/crates/biome_js_formatter/src/trivia.rs +++ b/crates/biome_js_formatter/src/trivia.rs @@ -58,10 +58,10 @@ pub(crate) struct CleanedNumberLiteralText<'a> { impl Format for CleanedNumberLiteralText<'_> { fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> { format_replaced( - &self.token, + self.token, &syntax_token_cow_slice( format_trimmed_number(self.token.text_trimmed(), self.options), - &self.token, + self.token, self.token.text_trimmed_range().start(), ), ) diff --git a/crates/biome_json_formatter/src/lib.rs b/crates/biome_json_formatter/src/lib.rs index d060bffffc5f..45b34fa25681 100644 --- a/crates/biome_json_formatter/src/lib.rs +++ b/crates/biome_json_formatter/src/lib.rs @@ -303,26 +303,6 @@ impl FormatToken for FormatJsonSyntaxToken { } } -// -// impl FormatToken for FormatJsonSyntaxToken { -// type Context = JsonFormatContext; -// fn format_skipped_token_trivia( -// &self, -// f: &mut Formatter, -// token: &JsonSyntaxToken, -// ) -> FormatResult<()> { -// format_skipped_token_trivia(token).fmt(f) -// } -// -// fn format_trimmed_token( -// &self, -// f: &mut Formatter, -// token: &JsonSyntaxToken, -// ) -> FormatResult<()> { -// format_trimmed_token(token).fmt(f) -// } -// } - impl AsFormat for JsonSyntaxToken { type Format<'a> = FormatRefWithRule<'a, Self, FormatJsonSyntaxToken>; diff --git a/crates/biome_json_formatter/src/trivia.rs b/crates/biome_json_formatter/src/trivia.rs index e0f4523bea63..affae06a6c32 100644 --- a/crates/biome_json_formatter/src/trivia.rs +++ b/crates/biome_json_formatter/src/trivia.rs @@ -58,10 +58,10 @@ pub(crate) struct CleanedNumberLiteralText<'a> { impl Format for CleanedNumberLiteralText<'_> { fn fmt(&self, f: &mut JsonFormatter) -> FormatResult<()> { format_replaced( - &self.token, + self.token, &syntax_token_cow_slice( format_trimmed_number(self.token.text_trimmed(), self.options), - &self.token, + self.token, self.token.text_trimmed_range().start(), ), )