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..fcdb17b9 100644 --- a/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala +++ b/core/src/test/scala/org/typelevel/paiges/PaigesScalacheckTest.scala @@ -558,4 +558,42 @@ 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").foldLeft(0)((acc, line) => math.max(acc, line.length)) + + // 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)) + } + } + + test("Doc.textWithLine(\"\\n\", d) eq d") { + forAll { (doc: Doc) => + assert(Doc.textWithLine("\n", doc) eq doc) + } + } }