Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
feat(rome_json_formatter): JSON Formatting number #2570 (#4090)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov authored Dec 23, 2022
1 parent 9e1e779 commit c4eb1d5
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 73 deletions.
1 change: 1 addition & 0 deletions crates/rome_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions crates/rome_formatter/src/token/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod number;
pub(crate) mod string;
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
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(),
),
Expand All @@ -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>,
Expand All @@ -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();
Expand Down
36 changes: 36 additions & 0 deletions crates/rome_formatter/src/token/string.rs
Original file line number Diff line number Diff line change
@@ -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<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()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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;
Expand All @@ -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 {
Expand Down
7 changes: 2 additions & 5 deletions crates/rome_js_formatter/src/ts/types/number_literal_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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};

Expand All @@ -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?)]
]
}

Expand Down
1 change: 0 additions & 1 deletion crates/rome_js_formatter/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_json_formatter/src/json/value/number_value.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::prelude::*;
use rome_formatter::token::number::format_number_token;
use rome_json_syntax::JsonNumberValue;

#[derive(Debug, Clone, Default)]
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)
}
}
27 changes: 27 additions & 0 deletions crates/rome_json_formatter/tests/specs/json/number.json
Original file line number Diff line number Diff line change
@@ -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
]
81 changes: 81 additions & 0 deletions crates/rome_json_formatter/tests/specs/json/number.json.snap
Original file line number Diff line number Diff line change
@@ -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
]
```


Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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;",
Expand All @@ -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
Expand All @@ -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": " ",
Expand Down Expand Up @@ -178,9 +156,9 @@ info: json/json/pass1.json
1e1,
0.1e1,
1e-1,
1e00,
2e+00,
2e-00,
1,
2,
2,
"rosebud"
]
```
Expand Down
Loading

0 comments on commit c4eb1d5

Please sign in to comment.