Skip to content

Commit

Permalink
Merge pull request #1129 from ArminWiebigke/main
Browse files Browse the repository at this point in the history
Fix: Schema rendering problems with Windows line separators
  • Loading branch information
yanns authored Sep 4, 2024
2 parents 8e51c7b + 24be4fb commit 0af3c0e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package sangria.renderer

import sangria.ast.AstLocation
import sangria.util.StringUtil.{escapeBlockString, escapeString}
import sangria.ast._
import sangria.util.StringUtil.{escapeBlockString, escapeString, linesIterator}

object QueryRenderer {
val Pretty: QueryRendererConfig = QueryRendererConfig(
Expand Down Expand Up @@ -477,7 +476,7 @@ object QueryRenderer {
extraIndent: Boolean = true): String =
if (node.value.trim.nonEmpty) {
val ind = if (extraIndent) indent.incForce.str else indent.strForce
val lines = escapeBlockString(node.value).split("\n").iterator.map { line =>
val lines = linesIterator(escapeBlockString(node.value)).map { line =>
if (line.isEmpty) line // do not output lines with only whitespaces inside
else ind + line
}
Expand Down
25 changes: 25 additions & 0 deletions modules/core/src/main/scala/sangria/util/StringUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sangria.util
import sangria.since3_0_0

import java.util.Locale
import scala.collection.AbstractIterator
import scala.collection.mutable.ListBuffer

object StringUtil {
Expand Down Expand Up @@ -110,6 +111,30 @@ object StringUtil {
def charHex(ch: Char): String =
Integer.toHexString(ch).toUpperCase(Locale.ENGLISH)

// Redefine `linesIterator`, since the implementation provided by Scala is not consistent for our
// cross-compiled versions
def linesIterator(string: String): Iterator[String] = new AbstractIterator[String] {
def hasNext: Boolean = !done
def next(): String = if (done) Iterator.empty.next() else advance()

private def isLineBreak(c: Char) = c == '\r' || c == '\n'
private def isWindowsLineBreak(c1: Char, c2: Char) = c1 == '\r' && c2 == '\n'
private[this] val len = string.length
private[this] var index = 0
@`inline` private def done: Boolean = index >= len
private def advance(): String = {
val start = index
while (!done && !isLineBreak(string.charAt(index))) index += 1
val end = index
if (!done) {
val c = string.charAt(index)
index += 1
if (!done && isWindowsLineBreak(c, string.charAt(index))) index += 1
}
string.substring(start, end)
}
}

/** Produces the value of a block string from its parsed raw value, similar to Coffeescript's
* block string, Python's docstring trim or Ruby's strip_heredoc.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,53 @@ class QueryRendererSpec extends AnyWordSpec with Matchers with StringMatchers {
| # se
|}""".stripMargin)(after.being(strippedOfCarriageReturns))
}

"renders comments with various line separators correctly" in {
val simpleType = sangria.schema.ObjectType[Unit, Unit](
name = "SimpleType",
description = "A simple object type.",
fields = sangria.schema.fields[Unit, Unit](
sangria.schema.Field(
name = "value",
description = Some(
"A field with a multi-line description that contains different line breaks.\n" +
"LF:\n\n" +
"CRLF:\r\n\r\n" +
"CR:\r\r" +
"Empty lines above should have no indentation when rendered."),
fieldType = sangria.schema.StringType,
resolve = _ => "theValue"
)
)
)

val compactRendered =
QueryRenderer.render(simpleType.toAst, QueryRenderer.Compact)
val prettyRendered =
QueryRenderer.render(simpleType.toAst, QueryRenderer.Pretty)

compactRendered should equal(
""""A simple object type." type SimpleType {"A field with a multi-line description that contains different line breaks.\nLF:\n\nCRLF:\r\n\r\nCR:\r\rEmpty lines above should have no indentation when rendered." value:String!}""")

prettyRendered.replace(" ", "_") should equal(
Seq(
"\"A simple object type.\"",
"type SimpleType {",
" \"\"\"",
" A field with a multi-line description that contains different line breaks.",
" LF:",
"",
" CRLF:",
"",
" CR:",
"",
" Empty lines above should have no indentation when rendered.",
" \"\"\"",
" value: String!",
"}"
).mkString("\n").replace(" ", "_")
)(after.being(strippedOfCarriageReturns))
}
}
}
}

0 comments on commit 0af3c0e

Please sign in to comment.