@@ -20,6 +20,8 @@ import cc.*
2020import CaptureSet .Mutability
2121import Capabilities .*
2222
23+ import java .lang .StringBuilder
24+
2325class 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