feat(rome_json_formatter): JSON Formatting number
MODIFY
crates/rome_formatter/src/lib.rs
crates/rome_formatter/src/lib.rs
@@ -37,6 +37,7 @@ pub mod printed_tokens;
pub mod printer;
pub mod separated;
mod source_map;
+pub mod token;
pub mod trivia;
mod verbatim;

ADD
crates/rome_formatter/src/token/mod.rs
+pub mod number;
+pub(crate) mod string;

RENAME
crates/rome_js_formatter/src/utils/number_utils.rs
crates/rome_formatter/src/token/number.rs
-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::token::string::ToAsciiLowercaseCow;
+use rome_rowan::{Language, SyntaxToken};
use std::borrow::Cow;
use std::num::NonZeroUsize;
-pub(crate) struct CleanedNumberLiteralText<'token> {
- token: &'token JsSyntaxToken,
- text: Cow<'token, str>,
+use crate::prelude::*;
+use crate::{CstFormatContext, Format};
+
+pub fn format_number_token<L>(token: &SyntaxToken<L>) -> CleanedNumberLiteralText<L>
+where
+ L: Language,
+{
+ CleanedNumberLiteralText { token }
}
-impl Format<JsFormatContext> for CleanedNumberLiteralText<'_> {
- fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
+pub struct CleanedNumberLiteralText<'token, L>
+where
+ L: Language,
+{
+ token: &'token SyntaxToken<L>,
+}
+
+impl<L, C> Format<C> for CleanedNumberLiteralText<'_, L>
+where
+ L: Language + 'static,
+ C: CstFormatContext<Language = L>,
+{
+ fn fmt(&self, f: &mut Formatter<C>) -> FormatResult<()> {
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(),
),
@@ -30,25 +38,12 @@ impl Format<JsFormatContext> 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<NonZeroUsize>,
@@ -61,6 +56,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<str> {
+ use FormatNumberLiteralState::*;
+
let text = text.to_ascii_lowercase_cow();
let mut copied_or_ignored_chars = 0usize;
let mut iter = text.chars().enumerate();

ADD
crates/rome_formatter/src/token/string.rs
+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<str>;
+}
+
+impl ToAsciiLowercaseCow for str {
+ fn to_ascii_lowercase_cow(&self) -> Cow<str> {
+ 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<str> {
+ self.as_str().to_ascii_lowercase_cow()
+ }
+}

MODIFY
crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs
crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs
use crate::prelude::*;
-use crate::utils::number_utils::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<JsNumberLiteralExpression> 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 {

MODIFY
crates/rome_js_formatter/src/ts/types/number_literal_type.rs
crates/rome_js_formatter/src/ts/types/number_literal_type.rs
use crate::prelude::*;
+use rome_formatter::token::number::format_number_token;
use crate::parentheses::NeedsParentheses;
-use crate::utils::number_utils::CleanedNumberLiteralText;
use rome_formatter::write;
use rome_js_syntax::{JsSyntaxNode, TsNumberLiteralType, TsNumberLiteralTypeFields};
@@ -16,10 +16,7 @@ impl FormatNodeRule<TsNumberLiteralType> 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?)]
]
}

MODIFY
crates/rome_js_formatter/src/utils/mod.rs
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;

MODIFY
crates/rome_json_formatter/src/json/value/number_value.rs
crates/rome_json_formatter/src/json/value/number_value.rs
use crate::prelude::*;
+use rome_formatter::token::number::format_number_token;
use rome_json_syntax::JsonNumberValue;
#[derive(Debug, Clone, Default)]
@@ -6,6 +7,6 @@ pub(crate) struct FormatJsonNumberValue;
impl FormatNodeRule<JsonNumberValue> for FormatJsonNumberValue {
fn fmt_fields(&self, node: &JsonNumberValue, f: &mut JsonFormatter) -> FormatResult<()> {
- node.value_token()?.format().fmt(f)
+ format_number_token(&node.value_token()?).fmt(f)
}
}

ADD
crates/rome_json_formatter/tests/specs/json/number.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
+]

ADD
crates/rome_json_formatter/tests/specs/json/number.json.snap
+---
+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
+]
+```
+
+

MODIFY
crates/rome_json_formatter/tests/specs/prettier/json/json/pass1.json.snap
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": "&#34; \u0022 %22 0x22 034 &#x22;",
@@ -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"
]
```

MODIFY
crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap
crates/rome_json_formatter/tests/specs/prettier/json/range/unary-expression-2.json.snap
---
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:
--2.0
+-
-+2.00000
++2.0
```
# Output
```json
-
-2.00000
+2.0
```
# Errors

feat(rome_json_formatter): JSON Formatting number #2570 (#4090)