Skip to content

Commit

Permalink
Make empty arrays and dictionaries render compactly even when indent …
Browse files Browse the repository at this point in the history
…> 0 (#501)

Fixes #499

We take a similar strategy as we do for adding commas: rather than
calling `renderIndent` immediately after opening a new array/dict and at
`visitEnd`, we instead set an `indentBuffered` flag and defer the
`renderIndent` until later. Then each `flushBuffer` call can render the
indent where necessary, and `visitEnd` can perform some additional
checks that the collection is not empty before rendering the final
indent

Added some basic tests to `ExampleTests.scala`, since some of this
behavior is probably worth showing to users in the documentation
  • Loading branch information
lihaoyi authored Jul 9, 2023
1 parent 7d6ad35 commit ad066a7
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 7 deletions.
27 changes: 21 additions & 6 deletions ujson/templates/BaseElemRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ class BaseElemRenderer[T <: upickle.core.ElemOps.Output]
private[this] var visitingKey = false

private[this] var commaBuffered = false
private[this] var indentBuffered = false
private[this] var quoteBuffered = false

def flushBuffer() = {
if (commaBuffered) {
commaBuffered = false
elemBuilder.append(',')
}
if (indentBuffered){
indentBuffered = false
renderIndent()
}
if (quoteBuffered) {
Expand All @@ -47,16 +51,21 @@ class BaseElemRenderer[T <: upickle.core.ElemOps.Output]
elemBuilder.append('[')

depth += 1
renderIndent()
indentBuffered = true

def subVisitor = BaseElemRenderer.this

def visitValue(v: T, index: Int): Unit = {
flushBuffer()
commaBuffered = true
indentBuffered = true
}

def visitEnd(index: Int) = {
commaBuffered = false
depth -= 1
renderIndent()
if (indentBuffered && commaBuffered) renderIndent()
commaBuffered = false
indentBuffered = false
elemBuilder.append(']')
flushElemBuilder()
out
Expand All @@ -67,26 +76,32 @@ class BaseElemRenderer[T <: upickle.core.ElemOps.Output]
flushBuffer()
elemBuilder.append('{')
depth += 1
renderIndent()
indentBuffered = true

def subVisitor = BaseElemRenderer.this
def visitKey(index: Int) = {
quoteBuffered = true
visitingKey = true
BaseElemRenderer.this
}

def visitKeyValue(s: Any): Unit = {
elemBuilder.append('"')
visitingKey = false
elemBuilder.append(':')
if (indent != -1) elemBuilder.append(' ')
}

def visitValue(v: T, index: Int): Unit = {
commaBuffered = true
indentBuffered = true
}

def visitEnd(index: Int) = {
commaBuffered = false
depth -= 1
renderIndent()
if (indentBuffered && commaBuffered) renderIndent()
commaBuffered = false
indentBuffered = false
elemBuilder.append('}')
flushElemBuilder()
out
Expand Down
15 changes: 14 additions & 1 deletion upickle/test/src/upickle/example/ExampleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ object ExampleTests extends TestSuite {
write("omg") ==> "\"omg\""
}
test("seqs"){
write(Array.empty[Int]) ==> "[]"
write(Array(1, 2, 3)) ==> "[1,2,3]"

// You can pass in an `indent` parameter to format it nicely
write(Array.empty[Int], indent = 4) ==> "[]"
write(Array(1, 2, 3), indent = 4) ==>
"""[
| 1,
Expand All @@ -176,6 +178,18 @@ object ExampleTests extends TestSuite {
write(Map(1 -> 2, 3 -> 4)) ==> """{"1":2,"3":4}"""
write(Map("hello" -> "world")) ==> """{"hello":"world"}"""
write(Map(Seq(1, 2) -> Seq(3, 4))) ==> """[[[1,2],[3,4]]]"""
write(Map.empty[Int, Int]) ==> """{}"""
write(Map(Seq.empty[Int] -> Seq.empty[Int])) ==> """[[[],[]]]"""

write(Map(Seq.empty[Int] -> Seq.empty[Int]), indent = 4) ==>
"""[
| [
| [],
| []
| ]
|]""".stripMargin

write(Map.empty[Int, Int], indent = 4) ==> """{}"""
}
test("options"){
write(Some(1)) ==> "[1]"
Expand Down Expand Up @@ -227,7 +241,6 @@ object ExampleTests extends TestSuite {

write(Bar("bearrr", Seq(Foo(1), Foo(2), Foo(3)))) ==>
"""{"name":"bearrr","foos":[{"i":1},{"i":2},{"i":3}]}"""

}
test("null"){
write(Bar(null, Seq(Foo(1), null, Foo(3)))) ==>
Expand Down

0 comments on commit ad066a7

Please sign in to comment.