Skip to content

Commit 5bf0d9c

Browse files
committed
Improve printing of strings
1 parent 582366c commit 5bf0d9c

File tree

1 file changed

+62
-14
lines changed

1 file changed

+62
-14
lines changed

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import cc.*
2020
import CaptureSet.Mutability
2121
import Capabilities.*
2222

23+
import java.lang.StringBuilder
24+
2325
class PlainPrinter(_ctx: Context) extends Printer {
2426

2527
/** The context of all public methods in Printer and subclasses.
@@ -704,22 +706,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
704706

705707
def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D"
706708

707-
private def escapedChar(ch: Char): String = (ch: @switch) match {
708-
case '\b' => "\\b"
709-
case '\t' => "\\t"
710-
case '\n' => "\\n"
711-
case '\f' => "\\f"
712-
case '\r' => "\\r"
713-
case '"' => "\\\""
714-
case '\'' => "\\\'"
715-
case '\\' => "\\\\"
716-
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch)
717-
}
709+
private def escapedChar(ch: Char): String =
710+
if requiresFormat(ch) then
711+
val b = StringBuilder().append('\'')
712+
escapedChar(b, ch)
713+
b.append('\'').toString
714+
else
715+
"'" + ch + "'"
718716

719717
def toText(const: Constant): Text = const.tag match {
720-
case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"")
718+
case StringTag => stringText(escapedString(const.value.toString, quoted = true))
721719
case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]"
722-
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
720+
case CharTag => literalText(escapedChar(const.charValue))
723721
case LongTag => literalText(const.longValue.toString + "L")
724722
case DoubleTag => literalText(const.doubleValue.toString + "d")
725723
case FloatTag => literalText(const.floatValue.toString + "f")
@@ -741,7 +739,57 @@ class PlainPrinter(_ctx: Context) extends Printer {
741739
~ (if param.isTypeParam then "" else ": ")
742740
~ toText(param.paramInfo)
743741

744-
protected def escapedString(str: String): String = str flatMap escapedChar
742+
protected final def escapedString(str: String): String = escapedString(str, quoted = false)
743+
744+
private def requiresFormat(c: Char): Boolean = (c: @switch) match
745+
case '\b' | '\t' | '\n' | '\f' | '\r' | '"' | '\'' | '\\' => true
746+
case c => c.isControl
747+
748+
private def escapedString(text: String, quoted: Boolean): String =
749+
def mustBuild: Boolean =
750+
var i = 0
751+
while i < text.length do
752+
if requiresFormat(text.charAt(i)) then return true
753+
i += 1
754+
false
755+
if mustBuild then
756+
val b = StringBuilder(text.length + 16)
757+
if quoted then
758+
b.append('"')
759+
var i = 0
760+
while i < text.length do
761+
escapedChar(b, text.charAt(i))
762+
i += 1
763+
if quoted then
764+
b.append('"')
765+
b.toString
766+
else if quoted then "\"" + text + "\""
767+
else text
768+
769+
private def escapedChar(b: StringBuilder, c: Char): Unit =
770+
def quadNibble(b: StringBuilder, x: Int, i: Int): Unit =
771+
if i < 4 then
772+
quadNibble(b, x >> 4, i + 1)
773+
val n = x & 0xF
774+
val c = if (n < 10) '0' + n else 'a' + (n - 10)
775+
b.append(c.toChar)
776+
val replace = (c: @switch) match
777+
case '\b' => "\\b"
778+
case '\t' => "\\t"
779+
case '\n' => "\\n"
780+
case '\f' => "\\f"
781+
case '\r' => "\\r"
782+
case '"' => "\\\""
783+
case '\'' => "\\\'"
784+
case '\\' => "\\\\"
785+
case c =>
786+
if c.isControl then
787+
b.append("\\u")
788+
quadNibble(b, c.toInt, 0)
789+
else
790+
b.append(c)
791+
return
792+
b.append(replace)
745793

746794
def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep)
747795

0 commit comments

Comments
 (0)