fmt: preserve the multiline but single entry iterables (#8663)

Fixes #8557.

`opa fmt` collapsed single-entry arrays/objects into one line even when
the original source spanned multiple rows, making deeply nested
structures hard to read. This maintains the structure.

Signed-off-by: unichronic <ishuvam.pal@gmail.com>
This commit is contained in:
Shuvam Pal
2026-06-22 19:51:48 +05:30
committed by GitHub
parent f75131f0e5
commit 1dcdacbe32
9 changed files with 127 additions and 19 deletions
+10 -2
View File
@@ -1875,9 +1875,13 @@ func (w *writer) writeIterable(elements []any, last *ast.Location, close *ast.Lo
return nil, err
}
hasBoundaryComment := false
// If there are comments within the single line, don't collapse it and keep it as-is
// Return an error so that writeTerm will write the original formatting
if len(lines) == 1 {
if w.beforeEnd != nil {
hasBoundaryComment = w.beforeEnd.Location.Row >= last.Row && w.beforeEnd.Location.Row <= close.Row
}
for _, c := range comments {
if c.Location.Row > last.Row && c.Location.Row < close.Row {
return comments, unexpectedCommentError{
@@ -1885,10 +1889,14 @@ func (w *writer) writeIterable(elements []any, last *ast.Location, close *ast.Lo
newCommentRow: c.Location.Row,
}
}
if c.Location.Row == last.Row || c.Location.Row == close.Row {
hasBoundaryComment = true
}
}
}
if len(lines) > 1 {
isMultiline := len(lines) > 1 || (len(lines) == 1 && len(elements) > 0 && close.Row > last.Row && !hasBoundaryComment)
if isMultiline {
w.delayBeforeEnd()
w.startMultilineSeq()
}
@@ -1910,7 +1918,7 @@ func (w *writer) writeIterable(elements []any, last *ast.Location, close *ast.Lo
return nil, err
}
if len(lines) > 1 {
if isMultiline {
w.write(",")
w.endLine()
comments, err = w.insertComments(comments, close)
+14 -4
View File
@@ -44,9 +44,15 @@ r = y {
# Comment on else
else = y {
y = ["howdy"]
x = {"x": {"y": "z"}}
x = {
"x": {
"y": "z",
},
}
a = {
"a": {"b": "c"},
"a": {
"b": "c",
},
"b": "c", "c": [
1, 2,
3, 4,
@@ -231,11 +237,15 @@ union_list := [(declare3 | declare4)]
union_set_2 := {(declare3 | declare4)}
union_object_multi_line := {"response": (declare3 | declare4)}
union_object_multi_line := {
"response": (declare3 | declare4),
}
union_object_key := {(declare3 | declare4): "foo"}
union_object_key_multi_line := {(declare3 | declare4): "foo"}
union_object_key_multi_line := {
(declare3 | declare4): "foo",
}
# more comments!
# more comments!
@@ -1,20 +1,38 @@
package a
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
value := {"a": # test 1
"b"} # test 2
value := {"a": "b"} # test 1
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
value := {"a": # this is
{"b": # my ridiculous
{"c": # way of
"d"}}} # commenting code
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
p[{"a": #
"b"} #
@@ -7,7 +7,9 @@ single_line_with {
multi_line_with {
fn(1) with input.a as "a"
with input.b as "b"
with input.c as {"foo": "bar"}
with input.c as {
"foo": "bar",
}
with input.d as [
1,
2,
+14 -4
View File
@@ -41,9 +41,15 @@ r := y if {
# Comment on else
else := y if {
y = ["howdy"]
x = {"x": {"y": "z"}}
x = {
"x": {
"y": "z",
},
}
a = {
"a": {"b": "c"},
"a": {
"b": "c",
},
"b": "c", "c": [
1, 2,
3, 4,
@@ -222,11 +228,15 @@ union_list := [(declare3 | declare4)]
union_set_2 := {(declare3 | declare4)}
union_object_multi_line := {"response": (declare3 | declare4)}
union_object_multi_line := {
"response": (declare3 | declare4),
}
union_object_key := {(declare3 | declare4): "foo"}
union_object_key_multi_line := {(declare3 | declare4): "foo"}
union_object_key_multi_line := {
(declare3 | declare4): "foo",
}
# more comments!
# more comments!
@@ -4,14 +4,26 @@ p[{"a": #
"b"} #
] := true
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
value := {"a": # test 1
"b"} # test 2
value := {"a": "b"} # test 1
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
value := {"a": # this is
{"b": # my ridiculous
@@ -23,7 +35,13 @@ p := {"a": #
str := "my \n string"
}
value := {"a": {"b": {"c": "d"}}}
value := {
"a": {
"b": {
"c": "d",
},
},
}
f(_) := value if {
value := {"a": # this is
@@ -63,7 +81,9 @@ p := {"a": str} if { #
str := "my \n string"
}
p := {"a": str} if {
p := {
"a": str,
} if {
#
str := "my \n string"
}
@@ -0,0 +1,19 @@
package test
test_with_and_indentation if {
my_mock_object := {
"the_first_key": {
"a_nested_key": {
"and_sub_nested_key": [
{
"yet_another_key": [
"and_sub_nested_key_value", "foooo", "baaar",
],
},
],
}
}
}
_ = my_mock_object
}
@@ -0,0 +1,19 @@
package test
test_with_and_indentation if {
my_mock_object := {
"the_first_key": {
"a_nested_key": {
"and_sub_nested_key": [
{
"yet_another_key": [
"and_sub_nested_key_value", "foooo", "baaar",
],
},
],
},
},
}
_ = my_mock_object
}
@@ -7,7 +7,9 @@ single_line_with if {
multi_line_with if {
fn(1) with input.a as "a"
with input.b as "b"
with input.c as {"foo": "bar"}
with input.c as {
"foo": "bar",
}
with input.d as [
1,
2,