@@ -18,9 +18,8 @@ object Formatting {
18
18
object ShownDef :
19
19
/** Represents a value that has been "shown" and can be consumed by StringFormatter.
20
20
* 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. */
24
23
opaque type Shown = Any
25
24
object Shown :
26
25
given [A : Show ]: Conversion [A , Shown ] = Show [A ].show(_)
@@ -29,6 +28,14 @@ object Formatting {
29
28
/** Show a value T by returning a "shown" result. */
30
29
def show (x : T ): Shown
31
30
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
+
32
39
/** The base implementation, passing the argument to StringFormatter which will try to `.show` it. */
33
40
object ShowAny extends Show [Any ]:
34
41
def show (x : Any ): Shown = x
@@ -37,11 +44,7 @@ object Formatting {
37
44
given Show [Product ] = ShowAny
38
45
39
46
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
45
48
46
49
class ShowImplicits1 extends ShowImplicits2 :
47
50
given Show [ImplicitRef ] = ShowAny
@@ -52,10 +55,12 @@ object Formatting {
52
55
inline def apply [A ](using inline z : Show [A ]): Show [A ] = z
53
56
54
57
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)
56
60
57
61
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))
59
64
60
65
given [X : Show ]: Show [X | Null ] with
61
66
def show (x : X | Null ) = if x == null then " null" else Show [X ].show(x.nn)
@@ -71,6 +76,7 @@ object Formatting {
71
76
given Show [Int ] = ShowAny
72
77
given Show [Char ] = ShowAny
73
78
given Show [Boolean ] = ShowAny
79
+ given Show [Integer ] = ShowAny
74
80
given Show [String ] = ShowAny
75
81
given Show [Class [? ]] = ShowAny
76
82
given Show [Throwable ] = ShowAny
@@ -84,6 +90,11 @@ object Formatting {
84
90
given Show [util.SourceFile ] = ShowAny
85
91
given Show [util.Spans .Span ] = ShowAny
86
92
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
87
98
end Show
88
99
end ShownDef
89
100
export ShownDef .{ Show , Shown }
@@ -100,15 +111,14 @@ object Formatting {
100
111
class StringFormatter (protected val sc : StringContext ) {
101
112
protected def showArg (arg : Any )(using Context ): String = arg.tryToShow
102
113
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 ))
109
119
case arg : Seq [? ] =>
110
120
(arg.map(showArg).mkString(" [" , " , " , " ]" ), suffix)
111
- case _ =>
121
+ case arg =>
112
122
(showArg(arg), suffix)
113
123
}
114
124
@@ -134,11 +144,13 @@ object Formatting {
134
144
* like concatenation, stripMargin etc on the values returned by em"...", and in the current error
135
145
* message composition methods, this is crucial.
136
146
*/
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))
140
152
141
- private def wrapNonSensical (arg : Any , str : String )(using Context ): String = {
153
+ private def wrapNonSensical (arg : Any , text : Text )(using Context ): Text = {
142
154
import Message ._
143
155
def isSensical (arg : Any ): Boolean = arg match {
144
156
case tpe : Type =>
@@ -151,8 +163,8 @@ object Formatting {
151
163
case _ => true
152
164
}
153
165
154
- if (isSensical(arg)) str
155
- else nonSensicalStartTag + str + nonSensicalEndTag
166
+ if (isSensical(arg)) text
167
+ else nonSensicalStartTag ~ text ~ nonSensicalEndTag
156
168
}
157
169
158
170
private type Recorded = Symbol | ParamRef | SkolemType
@@ -203,7 +215,7 @@ object Formatting {
203
215
}
204
216
}
205
217
206
- private class ExplainingPrinter (seen : Seen )(_ctx : Context ) extends RefinedPrinter (_ctx) {
218
+ private class ExplainingPrinter (seen : Seen )(_ctx : Context ) extends ErrorMessagePrinter (_ctx) {
207
219
208
220
/** True if printer should a source module instead of its module class */
209
221
private def useSourceModule (sym : Symbol ): Boolean =
@@ -307,9 +319,12 @@ object Formatting {
307
319
}
308
320
309
321
private def errorMessageCtx (using Context ): Context =
310
- ctx.property(MessageLimiter ) match
322
+ val ctx1 = ctx.property(MessageLimiter ) match
311
323
case Some (_ : ErrorMessageLimiter ) => ctx
312
324
case _ => ctx.fresh.setProperty(MessageLimiter , ErrorMessageLimiter ())
325
+ ctx1.printer match
326
+ case _ : ErrorMessagePrinter => ctx1
327
+ case _ => ctx1.fresh.setPrinterFn(ctx => ErrorMessagePrinter (ctx))
313
328
314
329
/** Context with correct printer set for explanations */
315
330
private def explainCtx (seen : Seen )(using Context ): Context =
@@ -364,8 +379,8 @@ object Formatting {
364
379
* highlight the difference
365
380
*/
366
381
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
369
384
370
385
DiffUtil .mkColoredTypeDiff(fnd, exp) match {
371
386
case _ if ctx.settings.color.value == " never" => (fnd, exp)
0 commit comments