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