Skip to content

Commit ed747fd

Browse files
committed
Improve printing of strings
1 parent 0a7f843 commit ed747fd

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.
@@ -694,22 +696,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
694696

695697
def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D"
696698

697-
private def escapedChar(ch: Char): String = (ch: @switch) match {
698-
case '\b' => "\\b"
699-
case '\t' => "\\t"
700-
case '\n' => "\\n"
701-
case '\f' => "\\f"
702-
case '\r' => "\\r"
703-
case '"' => "\\\""
704-
case '\'' => "\\\'"
705-
case '\\' => "\\\\"
706-
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch)
707-
}
699+
private def escapedChar(ch: Char): String =
700+
if requiresFormat(ch) then
701+
val b = StringBuilder().append('\'')
702+
escapedChar(b, ch)
703+
b.append('\'').toString
704+
else
705+
"'" + ch + "'"
708706

709707
def toText(const: Constant): Text = const.tag match {
710-
case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"")
708+
case StringTag => stringText(escapedString(const.value.toString, quoted = true))
711709
case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]"
712-
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
710+
case CharTag => literalText(escapedChar(const.charValue))
713711
case LongTag => literalText(const.longValue.toString + "L")
714712
case DoubleTag => literalText(const.doubleValue.toString + "d")
715713
case FloatTag => literalText(const.floatValue.toString + "f")
@@ -731,7 +729,57 @@ class PlainPrinter(_ctx: Context) extends Printer {
731729
~ (if param.isTypeParam then "" else ": ")
732730
~ toText(param.paramInfo)
733731

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

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

0 commit comments

Comments
 (0)