From 81b4ea5398cf28bc253ba78b527e0178b3fc039e Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Thu, 22 Dec 2022 17:12:53 +0200 Subject: [PATCH 1/2] feat(rome_json_formatter): JSON Formatting number #2570 --- crates/rome_formatter/src/lib.rs | 1 + crates/rome_formatter/src/utils/mod.rs | 2 + .../src/utils/number.rs} | 59 +++++++------- crates/rome_formatter/src/utils/string.rs | 36 +++++++++ .../expressions/number_literal_expression.rs | 2 +- .../src/ts/types/number_literal_type.rs | 2 +- crates/rome_js_formatter/src/utils/mod.rs | 1 - .../src/json/value/number_value.rs | 3 +- .../tests/specs/json/number.json | 27 +++++++ .../tests/specs/json/number.json.snap | 81 +++++++++++++++++++ .../specs/prettier/json/json/pass1.json.snap | 38 ++------- .../json/range/unary-expression-2.json.snap | 7 +- 12 files changed, 193 insertions(+), 66 deletions(-) create mode 100644 crates/rome_formatter/src/utils/mod.rs rename crates/{rome_js_formatter/src/utils/number_utils.rs => rome_formatter/src/utils/number.rs} (92%) create mode 100644 crates/rome_formatter/src/utils/string.rs create mode 100644 crates/rome_json_formatter/tests/specs/json/number.json create mode 100644 crates/rome_json_formatter/tests/specs/json/number.json.snap diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs index 8ac338e9517..a7bd501bcbe 100644 --- a/crates/rome_formatter/src/lib.rs +++ b/crates/rome_formatter/src/lib.rs @@ -38,6 +38,7 @@ pub mod printer; pub mod separated; mod source_map; pub mod trivia; +pub mod utils; mod verbatim; use crate::formatter::Formatter; diff --git a/crates/rome_formatter/src/utils/mod.rs b/crates/rome_formatter/src/utils/mod.rs new file mode 100644 index 00000000000..6cdb135d40a --- /dev/null +++ b/crates/rome_formatter/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod number; +pub(crate) mod string; diff --git a/crates/rome_js_formatter/src/utils/number_utils.rs b/crates/rome_formatter/src/utils/number.rs similarity index 92% rename from crates/rome_js_formatter/src/utils/number_utils.rs rename to crates/rome_formatter/src/utils/number.rs index 81e2dce9385..6990317a71a 100644 --- a/crates/rome_js_formatter/src/utils/number_utils.rs +++ b/crates/rome_formatter/src/utils/number.rs @@ -1,23 +1,37 @@ -use crate::prelude::*; -use crate::utils::string_utils::ToAsciiLowercaseCow; -use crate::JsFormatContext; -use crate::JsFormatter; -use rome_formatter::trivia::format_replaced; -use rome_formatter::Format; -use rome_formatter::FormatResult; -use rome_js_syntax::JsSyntaxKind::JS_NUMBER_LITERAL; -use rome_js_syntax::JsSyntaxKind::TS_NUMBER_LITERAL_TYPE; -use rome_js_syntax::JsSyntaxToken; +use crate::utils::string::ToAsciiLowercaseCow; +use rome_rowan::{Language, SyntaxToken}; use std::borrow::Cow; use std::num::NonZeroUsize; -pub(crate) struct CleanedNumberLiteralText<'token> { - token: &'token JsSyntaxToken, +use crate::prelude::*; +use crate::{CstFormatContext, Format}; + +pub struct CleanedNumberLiteralText<'token, L> +where + L: Language, +{ + token: &'token SyntaxToken, text: Cow<'token, str>, } -impl Format for CleanedNumberLiteralText<'_> { - fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> { +impl<'token, L> CleanedNumberLiteralText<'token, L> +where + L: Language, +{ + pub fn from_number_literal_token(token: &'token SyntaxToken) -> Self { + CleanedNumberLiteralText { + token, + text: format_trimmed_number(token.text_trimmed()), + } + } +} + +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( @@ -30,25 +44,12 @@ impl Format for CleanedNumberLiteralText<'_> { } } -impl<'token> CleanedNumberLiteralText<'token> { - pub fn from_number_literal_token(token: &'token JsSyntaxToken) -> Self { - debug_assert!(matches!( - &token.kind(), - JS_NUMBER_LITERAL | TS_NUMBER_LITERAL_TYPE - )); - CleanedNumberLiteralText { - token, - text: format_trimmed_number(token.text_trimmed()), - } - } -} - enum FormatNumberLiteralState { IntegerPart, DecimalPart(FormatNumberLiteralDecimalPart), Exponent(FormatNumberLiteralExponent), } -use FormatNumberLiteralState::*; + struct FormatNumberLiteralDecimalPart { dot_index: usize, last_non_zero_index: Option, @@ -61,6 +62,8 @@ struct FormatNumberLiteralExponent { } // Regex-free version of https://github.com/prettier/prettier/blob/ca246afacee8e6d5db508dae01730c9523bbff1d/src/common/util.js#L341-L356 fn format_trimmed_number(text: &str) -> Cow { + use FormatNumberLiteralState::*; + let text = text.to_ascii_lowercase_cow(); let mut copied_or_ignored_chars = 0usize; let mut iter = text.chars().enumerate(); diff --git a/crates/rome_formatter/src/utils/string.rs b/crates/rome_formatter/src/utils/string.rs new file mode 100644 index 00000000000..82fff862bcb --- /dev/null +++ b/crates/rome_formatter/src/utils/string.rs @@ -0,0 +1,36 @@ +use std::borrow::Cow; + +pub trait ToAsciiLowercaseCow { + /// Returns the same value as String::to_lowercase. The only difference + /// is that this functions returns ```Cow``` and does not allocate + /// if the string is already in lowercase. + fn to_ascii_lowercase_cow(&self) -> Cow; +} + +impl ToAsciiLowercaseCow for str { + fn to_ascii_lowercase_cow(&self) -> Cow { + debug_assert!(self.is_ascii()); + + let bytes = self.as_bytes(); + + for idx in 0..bytes.len() { + let chr = bytes[idx]; + if chr != chr.to_ascii_lowercase() { + let mut s = bytes.to_vec(); + for b in &mut s[idx..] { + b.make_ascii_lowercase(); + } + return Cow::Owned(unsafe { String::from_utf8_unchecked(s) }); + } + } + + Cow::Borrowed(self) + } +} + +impl ToAsciiLowercaseCow for String { + #[inline(always)] + fn to_ascii_lowercase_cow(&self) -> Cow { + self.as_str().to_ascii_lowercase_cow() + } +} diff --git a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs index cf22aad81df..7a02072802e 100644 --- a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use crate::utils::number_utils::CleanedNumberLiteralText; +use rome_formatter::utils::number::CleanedNumberLiteralText; use crate::parentheses::{is_member_object, NeedsParentheses}; use rome_js_syntax::JsNumberLiteralExpression; diff --git a/crates/rome_js_formatter/src/ts/types/number_literal_type.rs b/crates/rome_js_formatter/src/ts/types/number_literal_type.rs index 9543ef59c77..a8ebcf2ed7b 100644 --- a/crates/rome_js_formatter/src/ts/types/number_literal_type.rs +++ b/crates/rome_js_formatter/src/ts/types/number_literal_type.rs @@ -1,7 +1,7 @@ use crate::prelude::*; +use rome_formatter::utils::number::CleanedNumberLiteralText; use crate::parentheses::NeedsParentheses; -use crate::utils::number_utils::CleanedNumberLiteralText; use rome_formatter::write; use rome_js_syntax::{JsSyntaxNode, TsNumberLiteralType, TsNumberLiteralTypeFields}; diff --git a/crates/rome_js_formatter/src/utils/mod.rs b/crates/rome_js_formatter/src/utils/mod.rs index 3134a1fafb2..be6a0b2a51d 100644 --- a/crates/rome_js_formatter/src/utils/mod.rs +++ b/crates/rome_js_formatter/src/utils/mod.rs @@ -2,7 +2,6 @@ pub(crate) mod array; mod assignment_like; mod binary_like_expression; mod conditional; -pub(crate) mod number_utils; pub mod string_utils; pub(crate) mod format_class; diff --git a/crates/rome_json_formatter/src/json/value/number_value.rs b/crates/rome_json_formatter/src/json/value/number_value.rs index 3d05e07d2a8..19df02d6fca 100644 --- a/crates/rome_json_formatter/src/json/value/number_value.rs +++ b/crates/rome_json_formatter/src/json/value/number_value.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use rome_formatter::utils::number::CleanedNumberLiteralText; use rome_json_syntax::JsonNumberValue; #[derive(Debug, Clone, Default)] @@ -6,6 +7,6 @@ pub(crate) struct FormatJsonNumberValue; impl FormatNodeRule for FormatJsonNumberValue { fn fmt_fields(&self, node: &JsonNumberValue, f: &mut JsonFormatter) -> FormatResult<()> { - node.value_token()?.format().fmt(f) + CleanedNumberLiteralText::from_number_literal_token(&node.value_token()?).fmt(f) } } diff --git a/crates/rome_json_formatter/tests/specs/json/number.json b/crates/rome_json_formatter/tests/specs/json/number.json new file mode 100644 index 00000000000..7e185425bb3 --- /dev/null +++ b/crates/rome_json_formatter/tests/specs/json/number.json @@ -0,0 +1,27 @@ +[ + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + " s p a c e d ": [ + 1, 2, 3, + + 4, 5, 6, 7 + ] + }, + 0.5, + 98.6, + 99.44, + 1066, + 1e1, + 0.1e1, + 1e-1, + 1, + 2, + 2, + 2.00000 +] diff --git a/crates/rome_json_formatter/tests/specs/json/number.json.snap b/crates/rome_json_formatter/tests/specs/json/number.json.snap new file mode 100644 index 00000000000..2ed3eff7920 --- /dev/null +++ b/crates/rome_json_formatter/tests/specs/json/number.json.snap @@ -0,0 +1,81 @@ +--- +source: crates/rome_formatter_test/src/snapshot_builder.rs +info: json/number.json +--- + +# Input + +```json +[ + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + " s p a c e d ": [ + 1, 2, 3, + + 4, 5, 6, 7 + ] + }, + 0.5, + 98.6, + 99.44, + 1066, + 1e1, + 0.1e1, + 1e-1, + 1, + 2, + 2, + 2.00000 +] + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Line width: 80 +----- + +```json +[ + { + "integer": 1234567890, + "real": -9876.54321, + "e": 0.123456789e-12, + "E": 1.23456789e34, + "": 23456789012e66, + "zero": 0, + "one": 1, + " s p a c e d ": [ + 1, 2, 3, + + 4, 5, 6, 7 + ] + }, + 0.5, + 98.6, + 99.44, + 1066, + 1e1, + 0.1e1, + 1e-1, + 1, + 2, + 2, + 2.0 +] +``` + + diff --git a/crates/rome_json_formatter/tests/specs/prettier/json/json/pass1.json.snap b/crates/rome_json_formatter/tests/specs/prettier/json/json/pass1.json.snap index 9437c677007..5517857da99 100644 --- a/crates/rome_json_formatter/tests/specs/prettier/json/json/pass1.json.snap +++ b/crates/rome_json_formatter/tests/specs/prettier/json/json/pass1.json.snap @@ -73,20 +73,7 @@ info: json/json/pass1.json ```diff --- Prettier +++ Rome -@@ -9,17 +9,17 @@ - null, - { - "integer": 1234567890, -- "real": -9876.54321, -+ "real": -9876.543210, - "e": 0.123456789e-12, -- "E": 1.23456789e34, -- "": 23456789012e66, -+ "E": 1.234567890E+34, -+ "": 23456789012E66, - "zero": 0, - "one": 1, - "space": " ", +@@ -19,7 +19,7 @@ "quote": "\"", "backslash": "\\", "controls": "\b\f\n\r\t", @@ -95,7 +82,7 @@ info: json/json/pass1.json "alpha": "abcdefghijklmnopqrstuvwyz", "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", "digit": "0123456789", -@@ -43,17 +43,18 @@ +@@ -43,11 +43,12 @@ "compact": [1, 2, 3, 4, 5, 6, 7], "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", "quotes": "" \u0022 %22 0x22 034 "", @@ -109,15 +96,6 @@ info: json/json/pass1.json 1066, 1e1, 0.1e1, - 1e-1, -- 1, -- 2, -- 2, -+ 1e00, -+ 2e+00, -+ 2e-00, - "rosebud" - ] ``` # Output @@ -134,10 +112,10 @@ info: json/json/pass1.json null, { "integer": 1234567890, - "real": -9876.543210, + "real": -9876.54321, "e": 0.123456789e-12, - "E": 1.234567890E+34, - "": 23456789012E66, + "E": 1.23456789e34, + "": 23456789012e66, "zero": 0, "one": 1, "space": " ", @@ -178,9 +156,9 @@ info: json/json/pass1.json 1e1, 0.1e1, 1e-1, - 1e00, - 2e+00, - 2e-00, + 1, + 2, + 2, "rosebud" ] ``` diff --git a/crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap b/crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap index dea573db691..e6146d091f2 100644 --- a/crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap +++ b/crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap @@ -1,7 +1,6 @@ --- source: crates/rome_formatter_test/src/snapshot_builder.rs -info: - test_file: json/range/unary-expression-2.json +info: json/range/unary-expression-2.json --- # Input @@ -21,14 +20,14 @@ info: @@ -1 +1,2 @@ --2.0 +- -+2.00000 ++2.0 ``` # Output ```json - -2.00000 +2.0 ``` # Errors From 4c3272a4f365b5b0a2291c6e73b14e425741be5e Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Fri, 23 Dec 2022 11:13:50 +0200 Subject: [PATCH 2/2] feat(rome_json_formatter): JSON Formatting number #2570 --- crates/rome_formatter/src/lib.rs | 2 +- .../rome_formatter/src/{utils => token}/mod.rs | 0 .../src/{utils => token}/number.rs | 18 ++++++------------ .../src/{utils => token}/string.rs | 0 .../expressions/number_literal_expression.rs | 4 ++-- .../src/ts/types/number_literal_type.rs | 7 ++----- .../src/json/value/number_value.rs | 4 ++-- 7 files changed, 13 insertions(+), 22 deletions(-) rename crates/rome_formatter/src/{utils => token}/mod.rs (100%) rename crates/rome_formatter/src/{utils => token}/number.rs (96%) rename crates/rome_formatter/src/{utils => token}/string.rs (100%) diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs index a7bd501bcbe..98dfe6d379f 100644 --- a/crates/rome_formatter/src/lib.rs +++ b/crates/rome_formatter/src/lib.rs @@ -37,8 +37,8 @@ pub mod printed_tokens; pub mod printer; pub mod separated; mod source_map; +pub mod token; pub mod trivia; -pub mod utils; mod verbatim; use crate::formatter::Formatter; diff --git a/crates/rome_formatter/src/utils/mod.rs b/crates/rome_formatter/src/token/mod.rs similarity index 100% rename from crates/rome_formatter/src/utils/mod.rs rename to crates/rome_formatter/src/token/mod.rs diff --git a/crates/rome_formatter/src/utils/number.rs b/crates/rome_formatter/src/token/number.rs similarity index 96% rename from crates/rome_formatter/src/utils/number.rs rename to crates/rome_formatter/src/token/number.rs index 6990317a71a..ce65a7d5203 100644 --- a/crates/rome_formatter/src/utils/number.rs +++ b/crates/rome_formatter/src/token/number.rs @@ -1,4 +1,4 @@ -use crate::utils::string::ToAsciiLowercaseCow; +use crate::token::string::ToAsciiLowercaseCow; use rome_rowan::{Language, SyntaxToken}; use std::borrow::Cow; use std::num::NonZeroUsize; @@ -6,24 +6,18 @@ use std::num::NonZeroUsize; use crate::prelude::*; use crate::{CstFormatContext, Format}; -pub struct CleanedNumberLiteralText<'token, L> +pub fn format_number_token(token: &SyntaxToken) -> CleanedNumberLiteralText where L: Language, { - token: &'token SyntaxToken, - text: Cow<'token, str>, + CleanedNumberLiteralText { token } } -impl<'token, L> CleanedNumberLiteralText<'token, L> +pub struct CleanedNumberLiteralText<'token, L> where L: Language, { - pub fn from_number_literal_token(token: &'token SyntaxToken) -> Self { - CleanedNumberLiteralText { - token, - text: format_trimmed_number(token.text_trimmed()), - } - } + token: &'token SyntaxToken, } impl Format for CleanedNumberLiteralText<'_, L> @@ -35,7 +29,7 @@ where format_replaced( self.token, &syntax_token_cow_slice( - self.text.clone(), + format_trimmed_number(self.token.text_trimmed()), self.token, self.token.text_trimmed_range().start(), ), diff --git a/crates/rome_formatter/src/utils/string.rs b/crates/rome_formatter/src/token/string.rs similarity index 100% rename from crates/rome_formatter/src/utils/string.rs rename to crates/rome_formatter/src/token/string.rs diff --git a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs index 7a02072802e..c8f8f99605a 100644 --- a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use rome_formatter::utils::number::CleanedNumberLiteralText; +use rome_formatter::token::number::format_number_token; use crate::parentheses::{is_member_object, NeedsParentheses}; use rome_js_syntax::JsNumberLiteralExpression; @@ -15,7 +15,7 @@ impl FormatNodeRule for FormatJsNumberLiteralExpressi f: &mut JsFormatter, ) -> FormatResult<()> { let JsNumberLiteralExpressionFields { value_token } = node.as_fields(); - CleanedNumberLiteralText::from_number_literal_token(&value_token?).fmt(f) + format_number_token(&value_token?).fmt(f) } fn needs_parentheses(&self, item: &JsNumberLiteralExpression) -> bool { diff --git a/crates/rome_js_formatter/src/ts/types/number_literal_type.rs b/crates/rome_js_formatter/src/ts/types/number_literal_type.rs index a8ebcf2ed7b..00adb519f00 100644 --- a/crates/rome_js_formatter/src/ts/types/number_literal_type.rs +++ b/crates/rome_js_formatter/src/ts/types/number_literal_type.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use rome_formatter::utils::number::CleanedNumberLiteralText; +use rome_formatter::token::number::format_number_token; use crate::parentheses::NeedsParentheses; use rome_formatter::write; @@ -16,10 +16,7 @@ impl FormatNodeRule for FormatTsNumberLiteralType { } = node.as_fields(); write![ f, - [ - minus_token.format(), - CleanedNumberLiteralText::from_number_literal_token(&literal_token?) - ] + [minus_token.format(), format_number_token(&literal_token?)] ] } diff --git a/crates/rome_json_formatter/src/json/value/number_value.rs b/crates/rome_json_formatter/src/json/value/number_value.rs index 19df02d6fca..9f0a21c3a4d 100644 --- a/crates/rome_json_formatter/src/json/value/number_value.rs +++ b/crates/rome_json_formatter/src/json/value/number_value.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use rome_formatter::utils::number::CleanedNumberLiteralText; +use rome_formatter::token::number::format_number_token; use rome_json_syntax::JsonNumberValue; #[derive(Debug, Clone, Default)] @@ -7,6 +7,6 @@ pub(crate) struct FormatJsonNumberValue; impl FormatNodeRule for FormatJsonNumberValue { fn fmt_fields(&self, node: &JsonNumberValue, f: &mut JsonFormatter) -> FormatResult<()> { - CleanedNumberLiteralText::from_number_literal_token(&node.value_token()?).fmt(f) + format_number_token(&node.value_token()?).fmt(f) } }