Skip to content

Commit c5da032

Browse files
som-snytttgodzik
authored andcommitted
Improve printing of strings
1 parent 0643fc5 commit c5da032

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
@@ -17,6 +17,8 @@ import scala.annotation.switch
1717
import config.{Config, Feature}
1818
import cc.{CapturingType, EventuallyCapturingType, CaptureSet, isBoxed}
1919

20+
import java.lang.StringBuilder
21+
2022
class PlainPrinter(_ctx: Context) extends Printer {
2123

2224
/** The context of all public methods in Printer and subclasses.
@@ -579,22 +581,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
579581

580582
def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D"
581583

582-
private def escapedChar(ch: Char): String = (ch: @switch) match {
583-
case '\b' => "\\b"
584-
case '\t' => "\\t"
585-
case '\n' => "\\n"
586-
case '\f' => "\\f"
587-
case '\r' => "\\r"
588-
case '"' => "\\\""
589-
case '\'' => "\\\'"
590-
case '\\' => "\\\\"
591-
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch).nn
592-
}
584+
private def escapedChar(ch: Char): String =
585+
if requiresFormat(ch) then
586+
val b = StringBuilder().append('\'')
587+
escapedChar(b, ch)
588+
b.append('\'').toString
589+
else
590+
"'" + ch + "'"
593591

594592
def toText(const: Constant): Text = const.tag match {
595-
case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"")
593+
case StringTag => stringText(escapedString(const.value.toString, quoted = true))
596594
case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]"
597-
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
595+
case CharTag => literalText(escapedChar(const.charValue))
598596
case LongTag => literalText(const.longValue.toString + "L")
599597
case DoubleTag => literalText(const.doubleValue.toString + "d")
600598
case FloatTag => literalText(const.floatValue.toString + "f")
@@ -612,7 +610,57 @@ class PlainPrinter(_ctx: Context) extends Printer {
612610
~ (if param.isTypeParam then "" else ": ")
613611
~ toText(param.paramInfo)
614612

615-
protected def escapedString(str: String): String = str flatMap escapedChar
613+
protected final def escapedString(str: String): String = escapedString(str, quoted = false)
614+
615+
private def requiresFormat(c: Char): Boolean = (c: @switch) match
616+
case '\b' | '\t' | '\n' | '\f' | '\r' | '"' | '\'' | '\\' => true
617+
case c => c.isControl
618+
619+
private def escapedString(text: String, quoted: Boolean): String =
620+
def mustBuild: Boolean =
621+
var i = 0
622+
while i < text.length do
623+
if requiresFormat(text.charAt(i)) then return true
624+
i += 1
625+
false
626+
if mustBuild then
627+
val b = StringBuilder(text.length + 16)
628+
if quoted then
629+
b.append('"')
630+
var i = 0
631+
while i < text.length do
632+
escapedChar(b, text.charAt(i))
633+
i += 1
634+
if quoted then
635+
b.append('"')
636+
b.toString
637+
else if quoted then "\"" + text + "\""
638+
else text
639+
640+
private def escapedChar(b: StringBuilder, c: Char): Unit =
641+
def quadNibble(b: StringBuilder, x: Int, i: Int): Unit =
642+
if i < 4 then
643+
quadNibble(b, x >> 4, i + 1)
644+
val n = x & 0xF
645+
val c = if (n < 10) '0' + n else 'a' + (n - 10)
646+
b.append(c.toChar)
647+
val replace = (c: @switch) match
648+
case '\b' => "\\b"
649+
case '\t' => "\\t"
650+
case '\n' => "\\n"
651+
case '\f' => "\\f"
652+
case '\r' => "\\r"
653+
case '"' => "\\\""
654+
case '\'' => "\\\'"
655+
case '\\' => "\\\\"
656+
case c =>
657+
if c.isControl then
658+
b.append("\\u")
659+
quadNibble(b, c.toInt, 0)
660+
else
661+
b.append(c)
662+
return
663+
b.append(replace)
616664

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

0 commit comments

Comments
 (0)