@@ -17,6 +17,8 @@ import scala.annotation.switch
1717import config .{Config , Feature }
1818import cc .*
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.
@@ -668,22 +670,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
668670
669671 def toText (denot : Denotation ): Text = toText(denot.symbol) ~ " /D"
670672
671- private def escapedChar (ch : Char ): String = (ch : @ switch) match {
672- case '\b ' => " \\ b"
673- case '\t ' => " \\ t"
674- case '\n ' => " \\ n"
675- case '\f ' => " \\ f"
676- case '\r ' => " \\ r"
677- case '"' => " \\\" "
678- case '\' ' => " \\\' "
679- case '\\ ' => " \\\\ "
680- case _ => if ch.isControl then f " ${" \\ " }u ${ch.toInt}%04x " else String .valueOf(ch).nn
681- }
673+ private def escapedChar (ch : Char ): String =
674+ if requiresFormat(ch) then
675+ val b = StringBuilder ().append('\' ' )
676+ escapedChar(b, ch)
677+ b.append('\' ' ).toString
678+ else
679+ " '" + ch + " '"
682680
683681 def toText (const : Constant ): Text = const.tag match {
684- case StringTag => stringText(" \" " + escapedString(const.value.toString) + " \" " )
682+ case StringTag => stringText(escapedString(const.value.toString, quoted = true ) )
685683 case ClazzTag => " classOf[" ~ toText(const.typeValue) ~ " ]"
686- case CharTag => literalText(s " ' ${ escapedChar(const.charValue)} ' " )
684+ case CharTag => literalText(escapedChar(const.charValue))
687685 case LongTag => literalText(const.longValue.toString + " L" )
688686 case DoubleTag => literalText(const.doubleValue.toString + " d" )
689687 case FloatTag => literalText(const.floatValue.toString + " f" )
@@ -701,7 +699,57 @@ class PlainPrinter(_ctx: Context) extends Printer {
701699 ~ (if param.isTypeParam then " " else " : " )
702700 ~ toText(param.paramInfo)
703701
704- protected def escapedString (str : String ): String = str flatMap escapedChar
702+ protected final def escapedString (str : String ): String = escapedString(str, quoted = false )
703+
704+ private def requiresFormat (c : Char ): Boolean = (c : @ switch) match
705+ case '\b ' | '\t ' | '\n ' | '\f ' | '\r ' | '"' | '\' ' | '\\ ' => true
706+ case c => c.isControl
707+
708+ private def escapedString (text : String , quoted : Boolean ): String =
709+ def mustBuild : Boolean =
710+ var i = 0
711+ while i < text.length do
712+ if requiresFormat(text.charAt(i)) then return true
713+ i += 1
714+ false
715+ if mustBuild then
716+ val b = StringBuilder (text.length + 16 )
717+ if quoted then
718+ b.append('"' )
719+ var i = 0
720+ while i < text.length do
721+ escapedChar(b, text.charAt(i))
722+ i += 1
723+ if quoted then
724+ b.append('"' )
725+ b.toString
726+ else if quoted then " \" " + text + " \" "
727+ else text
728+
729+ private def escapedChar (b : StringBuilder , c : Char ): Unit =
730+ def quadNibble (b : StringBuilder , x : Int , i : Int ): Unit =
731+ if i < 4 then
732+ quadNibble(b, x >> 4 , i + 1 )
733+ val n = x & 0xF
734+ val c = if (n < 10 ) '0' + n else 'a' + (n - 10 )
735+ b.append(c.toChar)
736+ val replace = (c : @ switch) match
737+ case '\b ' => " \\ b"
738+ case '\t ' => " \\ t"
739+ case '\n ' => " \\ n"
740+ case '\f ' => " \\ f"
741+ case '\r ' => " \\ r"
742+ case '"' => " \\\" "
743+ case '\' ' => " \\\' "
744+ case '\\ ' => " \\\\ "
745+ case c =>
746+ if c.isControl then
747+ b.append(" \\ u" )
748+ quadNibble(b, c.toInt, 0 )
749+ else
750+ b.append(c)
751+ return
752+ b.append(replace)
705753
706754 def dclsText (syms : List [Symbol ], sep : String ): Text = Text (syms map dclText, sep)
707755
0 commit comments