From a73cdf15b25e31c42fbd61201766f4ad7b08f55a Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Wed, 25 Dec 2024 13:22:33 -1000 Subject: [PATCH 1/3] add Doc.textWithLine for constrolling how lines are parsed --- .../main/scala/org/typelevel/paiges/Doc.scala | 23 +++++++++---- .../paiges/PaigesScalacheckTest.scala | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/org/typelevel/paiges/Doc.scala b/core/src/main/scala/org/typelevel/paiges/Doc.scala index 263659e4..46d8ebaf 100644 --- a/core/src/main/scala/org/typelevel/paiges/Doc.scala +++ b/core/src/main/scala/org/typelevel/paiges/Doc.scala @@ -863,11 +863,11 @@ object Doc { /** * Convert a string to text. * - * This method translates newlines into an appropriate document - * representation. The result may be much more complex than a single - * `Text(_)` node. + * This method translates newlines into the given + * Doc, which should represent a new line and may be + * Doc.line, Doc.hardLine, Doc.lineOrSpace */ - def text(str: String): Doc = { + def textWithLine(str: String, line: Doc): Doc = { def tx(i: Int, j: Int): Doc = if (i == j) Empty else Text(str.substring(i, j)) @@ -881,16 +881,25 @@ object Doc { case _ => parse(i - 1, limit, doc) } - if (str == "") Empty - else if (str.length == 1) { + val len = str.length + + if (len == 0) Empty + else if (len == 1) { val c = str.charAt(0) if ((' ' <= c) && (c <= '~')) charTable(c.toInt - 32) else if (c == '\n') line else Text(str) } else if (str.indexOf('\n') < 0) Text(str) - else parse(str.length - 1, str.length, Empty) + else parse(len - 1, len, Empty) } + /** + * Convert a string to text. + * + * This method translates newlines into Doc.line (which can be flattened). + */ + def text(str: String): Doc = textWithLine(str, line) + /** * Convert an arbitrary value to a Doc, using `toString`. * diff --git a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala index 668ee939..f8e312c9 100644 --- a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala +++ b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala @@ -558,4 +558,36 @@ class PaigesScalacheckTest extends OurFunSuite { test("Doc.defer(x).representation(true) = x.representation(true)") { forAll((x: Doc) => assertEq(Doc.defer(x).representation(true), x.representation(true))) } + + test("textWithLine with hardLine has lines <= textWithLine") { + forAll { (str: String, i0: Int) => + // make a number between 0 and 199 + val width = (i0 & 0x7fffffff) % 200 + val str0 = Doc.text(str).flatten + val str1 = Doc.textWithLine(str, Doc.line).flatten + val str2 = Doc.textWithLine(str, Doc.hardLine).flatten + + val r1 = str0.render(width) + val r2 = str1.render(width) + val r3 = str2.render(width) + assert(r1 == r2) + + def maxLineLen(str: String): Int = + str.split("\n").map(_.length).maxOption.getOrElse(0) + + // hard lines can't be combined + assert(maxLineLen(r1) >= maxLineLen(r3)) + } + } + + test("textWithLine is different from text when there is a line and we render wide") { + forAll { (str: String) => + val hasLine = str.exists(_ == '\n') + + val tStr = Doc.text(str).flatten.renderWideStream.mkString + val hlStr = Doc.textWithLine(str, Doc.hardLine).flatten.renderWideStream.mkString + + assert(hasLine == (tStr != hlStr)) + } + } } From 1f5caeab782488d026b94be4c2d9e344bc755a9d Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Wed, 25 Dec 2024 13:29:05 -1000 Subject: [PATCH 2/3] scalafmt --- .../test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala index f8e312c9..5585ae57 100644 --- a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala +++ b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala @@ -586,7 +586,7 @@ class PaigesScalacheckTest extends OurFunSuite { val tStr = Doc.text(str).flatten.renderWideStream.mkString val hlStr = Doc.textWithLine(str, Doc.hardLine).flatten.renderWideStream.mkString - + assert(hasLine == (tStr != hlStr)) } } From 0ad84e2156ead38fc3c38fd45d3374c184a4fa51 Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Wed, 25 Dec 2024 13:36:50 -1000 Subject: [PATCH 3/3] fix test, add another law --- .../scala/org/typelevel/paiges/PaigesScalacheckTest.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala index 5585ae57..fcdb17b9 100644 --- a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala +++ b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala @@ -573,7 +573,7 @@ class PaigesScalacheckTest extends OurFunSuite { assert(r1 == r2) def maxLineLen(str: String): Int = - str.split("\n").map(_.length).maxOption.getOrElse(0) + str.split("\n").foldLeft(0)((acc, line) => math.max(acc, line.length)) // hard lines can't be combined assert(maxLineLen(r1) >= maxLineLen(r3)) @@ -590,4 +590,10 @@ class PaigesScalacheckTest extends OurFunSuite { assert(hasLine == (tStr != hlStr)) } } + + test("Doc.textWithLine(\"\\n\", d) eq d") { + forAll { (doc: Doc) => + assert(Doc.textWithLine("\n", doc) eq doc) + } + } }