@@ -18,9 +18,8 @@ object Formatting {
1818 object ShownDef :
1919 /** Represents a value that has been "shown" and can be consumed by StringFormatter.
2020 * Not just a string because it may be a Seq that StringFormatter will intersperse with the trailing separator.
21- * Also, it's not a `String | Seq[String]` because then we'd need a Context to call `Showable#show`. We could
22- * make Context a requirement for a Show instance but then we'd have lots of instances instead of just one ShowAny
23- * instance. We could also try to make `Show#show` require the Context, but then that breaks the Conversion. */
21+ * It may also be a CtxShow, which allows the Show instance to finish showing the value with the string
22+ * interpolator's correct context, that is with non-sensical tagging, message limiting, explanations, etc. */
2423 opaque type Shown = Any
2524 object Shown :
2625 given [A : Show ]: Conversion [A , Shown ] = Show [A ].show(_)
@@ -29,6 +28,14 @@ object Formatting {
2928 /** Show a value T by returning a "shown" result. */
3029 def show (x : T ): Shown
3130
31+ trait CtxShow :
32+ def run (using Context ): Shown
33+
34+ extension (s : Shown )
35+ def ctxShow (using Context ): Shown = s match
36+ case cs : CtxShow => cs.run
37+ case _ => s
38+
3239 /** The base implementation, passing the argument to StringFormatter which will try to `.show` it. */
3340 object ShowAny extends Show [Any ]:
3441 def show (x : Any ): Shown = x
@@ -37,11 +44,7 @@ object Formatting {
3744 given Show [Product ] = ShowAny
3845
3946 class ShowImplicits2 extends ShowImplicits3 :
40- given Show [ParamInfo ] with
41- def show (x : ParamInfo ) = x match
42- case x : Symbol => Show [x.type ].show(x)
43- case x : LambdaParam => Show [x.type ].show(x)
44- case _ => ShowAny
47+ given Show [ParamInfo ] = ShowAny
4548
4649 class ShowImplicits1 extends ShowImplicits2 :
4750 given Show [ImplicitRef ] = ShowAny
@@ -52,10 +55,12 @@ object Formatting {
5255 inline def apply [A ](using inline z : Show [A ]): Show [A ] = z
5356
5457 given [X : Show ]: Show [Seq [X ]] with
55- def show (x : Seq [X ]) = x.map(Show [X ].show)
58+ def show (x : Seq [X ]) = new CtxShow :
59+ def run (using Context ) = x.map(show1)
5660
5761 given [A : Show , B : Show ]: Show [(A , B )] with
58- def show (x : (A , B )) = (Show [A ].show(x._1), Show [B ].show(x._2))
62+ def show (x : (A , B )) = new CtxShow :
63+ def run (using Context ) = (show1(x._1), show1(x._2))
5964
6065 given [X : Show ]: Show [X | Null ] with
6166 def show (x : X | Null ) = if x == null then " null" else Show [X ].show(x.nn)
@@ -71,6 +76,7 @@ object Formatting {
7176 given Show [Int ] = ShowAny
7277 given Show [Char ] = ShowAny
7378 given Show [Boolean ] = ShowAny
79+ given Show [Integer ] = ShowAny
7480 given Show [String ] = ShowAny
7581 given Show [Class [? ]] = ShowAny
7682 given Show [Throwable ] = ShowAny
@@ -84,6 +90,11 @@ object Formatting {
8490 given Show [util.SourceFile ] = ShowAny
8591 given Show [util.Spans .Span ] = ShowAny
8692 given Show [tasty.TreeUnpickler # OwnerTree ] = ShowAny
93+
94+ private def show1 [A : Show ](x : A )(using Context ) = show2(Show [A ].show(x).ctxShow)
95+ private def show2 (x : Shown )(using Context ): String = x match
96+ case seq : Seq [? ] => seq.map(show2).mkString(" [" , " , " , " ]" )
97+ case res => res.tryToShow
8798 end Show
8899 end ShownDef
89100 export ShownDef .{ Show , Shown }
@@ -100,15 +111,14 @@ object Formatting {
100111 class StringFormatter (protected val sc : StringContext ) {
101112 protected def showArg (arg : Any )(using Context ): String = arg.tryToShow
102113
103- private def treatArg (arg : Shown , suffix : String )(using Context ): (Any , String ) = arg match {
104- case arg : Seq [? ] if suffix.nonEmpty && suffix.head == '%' =>
105- val (rawsep, rest) = suffix.tail.span(_ != '%' )
106- val sep = StringContext .processEscapes(rawsep)
107- if (rest.nonEmpty) (arg.map(showArg).mkString(sep), rest.tail)
108- else (arg, suffix)
114+ private def treatArg (arg : Shown , suffix : String )(using Context ): (String , String ) = arg.ctxShow match {
115+ case arg : Seq [? ] if suffix.indexOf('%' ) == 0 && suffix.indexOf('%' , 1 ) != - 1 =>
116+ val end = suffix.indexOf('%' , 1 )
117+ val sep = StringContext .processEscapes(suffix.substring(1 , end))
118+ (arg.mkString(sep), suffix.substring(end + 1 ))
109119 case arg : Seq [? ] =>
110120 (arg.map(showArg).mkString(" [" , " , " , " ]" ), suffix)
111- case _ =>
121+ case arg =>
112122 (showArg(arg), suffix)
113123 }
114124
@@ -134,11 +144,13 @@ object Formatting {
134144 * like concatenation, stripMargin etc on the values returned by em"...", and in the current error
135145 * message composition methods, this is crucial.
136146 */
137- class ErrorMessageFormatter (sc : StringContext ) extends StringFormatter (sc):
138- override protected def showArg (arg : Any )(using Context ): String =
139- wrapNonSensical(arg, super .showArg(arg)(using errorMessageCtx))
147+ def forErrorMessages (op : Context ?=> String )(using Context ): String = op(using errorMessageCtx)
148+
149+ private class ErrorMessagePrinter (_ctx : Context ) extends RefinedPrinter (_ctx):
150+ override def toText (tp : Type ): Text = wrapNonSensical(tp, super .toText(tp))
151+ override def toText (sym : Symbol ): Text = wrapNonSensical(sym, super .toText(sym))
140152
141- private def wrapNonSensical (arg : Any , str : String )(using Context ): String = {
153+ private def wrapNonSensical (arg : Any , text : Text )(using Context ): Text = {
142154 import Message ._
143155 def isSensical (arg : Any ): Boolean = arg match {
144156 case tpe : Type =>
@@ -151,8 +163,8 @@ object Formatting {
151163 case _ => true
152164 }
153165
154- if (isSensical(arg)) str
155- else nonSensicalStartTag + str + nonSensicalEndTag
166+ if (isSensical(arg)) text
167+ else nonSensicalStartTag ~ text ~ nonSensicalEndTag
156168 }
157169
158170 private type Recorded = Symbol | ParamRef | SkolemType
@@ -203,7 +215,7 @@ object Formatting {
203215 }
204216 }
205217
206- private class ExplainingPrinter (seen : Seen )(_ctx : Context ) extends RefinedPrinter (_ctx) {
218+ private class ExplainingPrinter (seen : Seen )(_ctx : Context ) extends ErrorMessagePrinter (_ctx) {
207219
208220 /** True if printer should a source module instead of its module class */
209221 private def useSourceModule (sym : Symbol ): Boolean =
@@ -307,9 +319,12 @@ object Formatting {
307319 }
308320
309321 private def errorMessageCtx (using Context ): Context =
310- ctx.property(MessageLimiter ) match
322+ val ctx1 = ctx.property(MessageLimiter ) match
311323 case Some (_ : ErrorMessageLimiter ) => ctx
312324 case _ => ctx.fresh.setProperty(MessageLimiter , ErrorMessageLimiter ())
325+ ctx1.printer match
326+ case _ : ErrorMessagePrinter => ctx1
327+ case _ => ctx1.fresh.setPrinterFn(ctx => ErrorMessagePrinter (ctx))
313328
314329 /** Context with correct printer set for explanations */
315330 private def explainCtx (seen : Seen )(using Context ): Context =
@@ -364,8 +379,8 @@ object Formatting {
364379 * highlight the difference
365380 */
366381 def typeDiff (found : Type , expected : Type )(using Context ): (String , String ) = {
367- val fnd = wrapNonSensical(found, found.show)
368- val exp = wrapNonSensical(expected, expected.show)
382+ val fnd = wrapNonSensical(found, found.toText(ctx.printer)).show
383+ val exp = wrapNonSensical(expected, expected.toText(ctx.printer)).show
369384
370385 DiffUtil .mkColoredTypeDiff(fnd, exp) match {
371386 case _ if ctx.settings.color.value == " never" => (fnd, exp)
0 commit comments