From fe5852f958de534f9b478881d291278e2f4068cd Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 18 Feb 2026 02:13:18 -0600 Subject: [PATCH] Add compact JSON5 array formatting plan and update JSON serialization logic - Introduced a new plan for compacting JSON5 arrays, allowing arrays with fewer than 12 non-compound elements to render inline. - Updated the `write_to()` function in `src/json.rs` to implement this formatting rule, enhancing the output for specific JSON5 structures. - Adjusted test snapshots to reflect the new inline formatting for arrays, ensuring consistency across various JSON5 examples. These changes improve the readability and efficiency of JSON5 data representation, aligning with the new formatting rules. --- .../compact_json5_arrays_36a8cfab.plan.md | 328 ++++++++++++++++++ ...s-src__dir_with_meta__init.meta.json5.snap | 4 +- ...wares-src__project_json.project.json5.snap | 6 +- src/json.rs | 19 + 4 files changed, 349 insertions(+), 8 deletions(-) create mode 100644 .cursor/plans/compact_json5_arrays_36a8cfab.plan.md diff --git a/.cursor/plans/compact_json5_arrays_36a8cfab.plan.md b/.cursor/plans/compact_json5_arrays_36a8cfab.plan.md new file mode 100644 index 00000000..d2ae36ba --- /dev/null +++ b/.cursor/plans/compact_json5_arrays_36a8cfab.plan.md @@ -0,0 +1,328 @@ +--- +name: Compact JSON5 arrays +overview: Arrays with <12 non-compound elements render on one line. Recursive -- inner arrays compact independently. +todos: + - id: modify-array-arm + content: "Modify the Array arm of write_to() in src/json.rs: inline if <12 elements and no element is Array/Object" + status: completed + - id: snapshot-tests + content: Run cargo test, then cargo insta review to update affected snapshots + status: completed +isProject: false +--- + +# Compact JSON5 Array Formatting + +## Rule + +An array renders inline if: + +1. Fewer than 12 direct elements +2. No direct element is an `Array` or `Object` + +Format: `[ elem, elem, elem ]` (spaces inside brackets). Recursive -- each nested array independently decides. + +## Change + +One file: [src/json.rs](src/json.rs), the Array arm of `Json5Value::write_to()` (lines 154-169). ~10 lines added. + +--- + +## Expected Output Examples + +### Color3 (3 floats) + +Before: + +```json5 +Color3: [ + 0.41568630933761597, + 0, + 1 +] +``` + +After: + +```json5 +Color3: [ 0.41568630933761597, 0, 1 ] +``` + +### NumberRange (2 floats) + +Before: + +```json5 +Lifetime: { + NumberRange: [ + 0.25, + 0.6000000238418579 + ] +} +``` + +After: + +```json5 +Lifetime: { + NumberRange: [ 0.25, 0.6000000238418579 ] +} +``` + +### Tags (string array) + +Before: + +```json5 +Tags: [ + "SoundGroupEffects" +] +``` + +After: + +```json5 +Tags: [ "SoundGroupEffects" ] +``` + +Multiple tags: + +```json5 +Tags: [ "_VFX", "Collectible", "PowerUp" ] +``` + +### CFrame (object with position + orientation) + +Before: + +```json5 +CFrame: { + position: [ + -0.09659165, + 1.517803, + -0.83597946 + ], + orientation: [ + [ + 0.10630996, + -0.0024746126, + -0.9943304 + ], + [ + 0.021991052, + 0.999758, + -0.0021534874 + ], + [ + 0.9941102, + -0.021623673, + 0.1063388 + ] + ] +} +``` + +After: + +```json5 +CFrame: { + position: [ -0.09659165, 1.517803, -0.83597946 ], + orientation: [ + [ 0.10630996, -0.0024746126, -0.9943304 ], + [ 0.021991052, 0.999758, -0.0021534874 ], + [ 0.9941102, -0.021623673, 0.1063388 ] + ] +} +``` + +- `position` (3 numbers) -> inline +- `orientation` (3 arrays) -> multi-line because elements are arrays +- Each inner orientation row (3 numbers) -> inline + +### UDim2 (2 arrays of 2 numbers) + +Before: + +```json5 +Size: [ + [ + 1, + 0 + ], + [ + 0, + 100 + ] +] +``` + +After: + +```json5 +Size: [ + [ 1, 0 ], + [ 0, 100 ] +] +``` + +Outer has array elements -> multi-line. Inner arrays (2 numbers each) -> inline. + +### Rect (2 Vector2s) + +Before: + +```json5 +ImageRectOffset: [ + [ + 100, + 100 + ], + [ + 200, + 200 + ] +] +``` + +After: + +```json5 +ImageRectOffset: [ + [ 100, 100 ], + [ 200, 200 ] +] +``` + +### ColorSequence keypoints (array of objects -- NO CHANGE) + +Before and after (objects always expand): + +```json5 +Color: { + ColorSequence: { + keypoints: [ + { + color: [ 0.364705890417099, 0.11372549086809158, 1 ], + time: 0 + }, + { + color: [ 0.364705890417099, 0.11372549086809158, 1 ], + time: 1 + } + ] + } +} +``` + +- `keypoints` array has objects -> stays multi-line +- `color` arrays inside each keypoint (3 numbers) -> inline + +### NumberSequence with 20 keypoints (array of objects -- NO CHANGE) + +Stays fully expanded. Both because elements are objects AND count >= 12. + +### Full file: Texture.model.json5 + +Before: + +```json5 +{ + className: "Texture", + properties: { + Color3: [ + 0.41568630933761597, + 0, + 1 + ], + Face: "Top", + StudsPerTileU: 200, + StudsPerTileV: 200, + TextureContent: "rbxassetid://10025668350", + Transparency: 0.6000000238418579 + } +} +``` + +After: + +```json5 +{ + className: "Texture", + properties: { + Color3: [ 0.41568630933761597, 0, 1 ], + Face: "Top", + StudsPerTileU: 200, + StudsPerTileV: 200, + TextureContent: "rbxassetid://10025668350", + Transparency: 0.6000000238418579 + } +} +``` + +### Full file: Water.model.json5 + +Before: + +```json5 +{ + attributes: { + Rojo_Ref_SoundGroup: "ServerStorage/__server_muted.model.json5" + }, + className: "Sound", + properties: { + AudioContent: "rbxassetid://9120549564", + Looped: true, + Playing: true, + SourceAssetId: 9120549564, + Tags: [ + "SoundGroupEffects" + ], + Volume: 3 + } +} +``` + +After: + +```json5 +{ + attributes: { + Rojo_Ref_SoundGroup: "ServerStorage/__server_muted.model.json5" + }, + className: "Sound", + properties: { + AudioContent: "rbxassetid://9120549564", + Looped: true, + Playing: true, + SourceAssetId: 9120549564, + Tags: [ "SoundGroupEffects" ], + Volume: 3 + } +} +``` + +### Edge: empty array (NO CHANGE) + +```json5 +Tags: [] +``` + +### Edge: 12+ primitive elements (stays multi-line) + +```json5 +SomeList: [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 +] +``` + diff --git a/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__dir_with_meta__init.meta.json5.snap b/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__dir_with_meta__init.meta.json5.snap index 55faa78c..43e02a85 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__dir_with_meta__init.meta.json5.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__dir_with_meta__init.meta.json5.snap @@ -5,8 +5,6 @@ expression: src/dir_with_meta/init.meta.json5 { className: "Configuration", properties: { - Tags: [ - "This tag on dir_with_meta" - ] + Tags: [ "This tag on dir_with_meta" ] } } diff --git a/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__project_json.project.json5.snap b/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__project_json.project.json5.snap index 9280fd77..b1202688 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__project_json.project.json5.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__tests__syncback__json_middlewares-src__project_json.project.json5.snap @@ -7,11 +7,7 @@ expression: src/project_json.project.json5 tree: { $className: "Color3Value", $properties: { - Value: [ - 1337, - -1337, - 1337 - ] + Value: [ 1337, -1337, 1337 ] } } } diff --git a/src/json.rs b/src/json.rs index 058c6278..8303c91e 100644 --- a/src/json.rs +++ b/src/json.rs @@ -154,6 +154,25 @@ impl Json5Value { Json5Value::Array(arr) => { if arr.is_empty() { output.push_str("[]"); + } else if arr.len() < 12 + && arr.iter().all(|v| { + matches!( + v, + Json5Value::Null + | Json5Value::Bool(_) + | Json5Value::Number(_) + | Json5Value::String(_) + ) + }) + { + output.push_str("[ "); + for (i, item) in arr.iter().enumerate() { + if i > 0 { + output.push_str(", "); + } + item.write_to(output, 0); + } + output.push_str(" ]"); } else { output.push_str("[\n"); for (i, item) in arr.iter().enumerate() {