Skip to content

Commit 9d11cf6

Browse files
committed
sort output before printing in REPL & move rendering
1 parent f7bee35 commit 9d11cf6

File tree

3 files changed

+86
-58
lines changed

3 files changed

+86
-58
lines changed

compiler/src/dotty/tools/repl/Rendering.scala

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ import java.io.{ StringWriter, PrintWriter }
55
import java.lang.{ ClassLoader, ExceptionInInitializerError }
66
import java.lang.reflect.InvocationTargetException
77

8+
import dotc.ast.tpd
89
import dotc.core.Contexts.Context
910
import dotc.core.Denotations.Denotation
1011
import dotc.core.Flags
11-
import dotc.core.Symbols.Symbol
12+
import dotc.core.Flags._
13+
import dotc.core.Symbols.{Symbol, defn}
1214
import dotc.core.StdNames.str
15+
import dotc.core.NameOps.NameDecorator
16+
import dotc.printing.ReplPrinter
17+
import dotc.reporting.{MessageRendering, Message, Diagnostic}
18+
import dotc.util.SourcePosition
1319

1420
/** This rendering object uses `ClassLoader`s to accomplish crossing the 4th
1521
* wall (i.e. fetching back values from the compiled class files put into a
@@ -21,8 +27,15 @@ import dotc.core.StdNames.str
2127
*/
2228
private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {
2329

30+
import Rendering._
31+
2432
private val MaxStringElements: Int = 1000 // no need to mkString billions of elements
2533

34+
/** A `MessageRenderer` for the REPL without file positions */
35+
private val messageRenderer = new MessageRendering {
36+
override def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context): String = ""
37+
}
38+
2639
private var myClassLoader: ClassLoader = _
2740

2841
private var myReplStringOf: Object => String = _
@@ -63,7 +76,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {
6376
* Calling this method evaluates the expression using reflection
6477
*/
6578
private def valueOf(sym: Symbol)(implicit ctx: Context): Option[String] = {
66-
val defn = ctx.definitions
6779
val objectName = sym.owner.fullName.encode.toString.stripSuffix("$")
6880
val resObj: Class[?] = Class.forName(objectName, true, classLoader())
6981
val value =
@@ -82,19 +94,33 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {
8294
}
8395
}
8496

97+
/** Formats errors using the `messageRenderer` */
98+
def formatError(dia: Diagnostic)(implicit state: State): Diagnostic =
99+
new Diagnostic(
100+
messageRenderer.messageAndPos(dia.msg, dia.pos, messageRenderer.diagnosticLevel(dia))(state.context),
101+
dia.pos,
102+
dia.level
103+
)
104+
105+
def renderTypeDef(d: Denotation)(implicit ctx: Context): Diagnostic =
106+
infoDiagnostic("// defined " ++ d.symbol.showUser, d)
107+
108+
def renderTypeAlias(d: Denotation)(implicit ctx: Context): Diagnostic =
109+
infoDiagnostic("// defined alias " ++ d.symbol.showUser, d)
110+
85111
/** Render method definition result */
86-
def renderMethod(d: Denotation)(implicit ctx: Context): String =
87-
d.symbol.showUser
112+
def renderMethod(d: Denotation)(implicit ctx: Context): Diagnostic =
113+
infoDiagnostic(d.symbol.showUser, d)
88114

89115
/** Render value definition result */
90-
def renderVal(d: Denotation)(implicit ctx: Context): Option[String] = {
116+
def renderVal(d: Denotation)(implicit ctx: Context): Option[Diagnostic] = {
91117
val dcl = d.symbol.showUser
92118

93119
try {
94-
if (d.symbol.is(Flags.Lazy)) Some(dcl)
95-
else valueOf(d.symbol).map(value => s"$dcl = $value")
120+
if (d.symbol.is(Flags.Lazy)) Some(infoDiagnostic(dcl, d))
121+
else valueOf(d.symbol).map(value => infoDiagnostic(s"$dcl = $value", d))
96122
}
97-
catch { case ex: InvocationTargetException => Some(renderError(ex)) }
123+
catch { case ex: InvocationTargetException => Some(infoDiagnostic(renderError(ex), d)) }
98124
}
99125

100126
/** Render the stack trace of the underlying exception */
@@ -108,4 +134,19 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {
108134
cause.printStackTrace(pw)
109135
sw.toString
110136
}
137+
138+
private def infoDiagnostic(msg: String, d: Denotation)(implicit ctx: Context): Diagnostic =
139+
new Diagnostic.Info(msg, d.symbol.sourcePos)
140+
141+
}
142+
143+
object Rendering {
144+
145+
implicit class ShowUser(val s: Symbol) extends AnyVal {
146+
def showUser(implicit ctx: Context): String = {
147+
val printer = new ReplPrinter(ctx)
148+
val text = printer.dclText(s)
149+
text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value)
150+
}
151+
}
111152
}

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class ReplDriver(settings: Array[String],
139139
// TODO: i5069
140140
final def bind(name: String, value: Any)(implicit state: State): State = state
141141

142+
// redirecting the output allows us to test `println` in scripted tests
142143
private def withRedirectedOutput(op: => State): State = {
143144
val savedOut = System.out
144145
val savedErr = System.err
@@ -238,19 +239,29 @@ class ReplDriver(settings: Array[String],
238239
allImports += (newState.objectIndex -> newImports)
239240
val newStateWithImports = newState.copy(imports = allImports)
240241

241-
val warnings = newState.context.reporter.removeBufferedMessages(newState.context)
242-
displayErrors(warnings)(newState) // display warnings
243-
implicit val ctx = newState.context
244-
if (!ctx.settings.XreplDisableDisplay.value)
245-
displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
246-
else
247-
newStateWithImports
242+
val warnings = newState.context.reporter
243+
.removeBufferedMessages(newState.context)
244+
.map(rendering.formatError)
245+
246+
implicit val ctx: Context = newState.context
247+
val (updatedState, definitions) =
248+
if (!ctx.settings.XreplDisableDisplay.value)
249+
renderDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
250+
else
251+
(newStateWithImports, Seq.empty)
252+
253+
254+
(definitions ++ warnings)
255+
.sortBy(_.pos.point) // ensure multi-line output is printed in the order it was typed in
256+
.map(_.msg)
257+
.foreach(out.println)
258+
259+
updatedState
248260
}
249261
)
250262
}
251263

252-
/** Display definitions from `tree` */
253-
private def displayDefinitions(tree: tpd.Tree, newestWrapper: Name)(implicit state: State): State = {
264+
private def renderDefinitions(tree: tpd.Tree, newestWrapper: Name)(implicit state: State): (State, Seq[Diagnostic]) = {
254265
implicit val ctx = state.context
255266

256267
def resAndUnit(denot: Denotation) = {
@@ -264,7 +275,7 @@ class ReplDriver(settings: Array[String],
264275
name.startsWith(str.REPL_RES_PREFIX) && hasValidNumber && sym.info == defn.UnitType
265276
}
266277

267-
def displayMembers(symbol: Symbol) = if (tree.symbol.info.exists) {
278+
def displayMembers(symbol: Symbol): (State, Seq[Diagnostic]) = if (tree.symbol.info.exists) {
268279
val info = symbol.info
269280
val defs =
270281
info.bounds.hi.finalResultType
@@ -274,51 +285,47 @@ class ReplDriver(settings: Array[String],
274285
denot.symbol.owner == defn.ObjectClass ||
275286
denot.symbol.isConstructor
276287
}
277-
.sortBy(_.name)
278288

279289
val vals =
280290
info.fields
281291
.filterNot(_.symbol.isOneOf(ParamAccessor | Private | Synthetic | Artifact | Module))
282292
.filter(_.symbol.name.is(SimpleNameKind))
283-
.sortBy(_.name)
284293

285294
val typeAliases =
286-
info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias).sortBy(_.name)
295+
info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias)
287296

288-
(
289-
typeAliases.map("// defined alias " + _.symbol.showUser) ++
297+
val formattedMembers =
298+
typeAliases.map(rendering.renderTypeAlias) ++
290299
defs.map(rendering.renderMethod) ++
291-
vals.map(rendering.renderVal).flatten
292-
).foreach(str => out.println(SyntaxHighlighting.highlight(str)))
300+
vals.flatMap(rendering.renderVal)
293301

294-
state.copy(valIndex = state.valIndex - vals.count(resAndUnit))
302+
(state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), formattedMembers)
295303
}
296-
else state
304+
else (state, Seq.empty)
297305

298306
def isSyntheticCompanion(sym: Symbol) =
299307
sym.is(Module) && sym.is(Synthetic)
300308

301-
def displayTypeDefs(sym: Symbol) = sym.info.memberClasses
309+
def typeDefs(sym: Symbol): Seq[Diagnostic] = sym.info.memberClasses
302310
.collect {
303311
case x if !isSyntheticCompanion(x.symbol) && !x.symbol.name.isReplWrapperName =>
304-
x.symbol
305-
}
306-
.foreach { sym =>
307-
out.println(SyntaxHighlighting.highlight("// defined " + sym.showUser))
312+
rendering.renderTypeDef(x)
308313
}
309314

310-
311315
ctx.atPhase(ctx.typerPhase.next) {
312316
// Display members of wrapped module:
313317
tree.symbol.info.memberClasses
314318
.find(_.symbol.name == newestWrapper.moduleClassName)
315319
.map { wrapperModule =>
316-
displayTypeDefs(wrapperModule.symbol)
317-
displayMembers(wrapperModule.symbol)
320+
val formattedTypeDefs = typeDefs(wrapperModule.symbol)
321+
val (newState, formattedMembers) = displayMembers(wrapperModule.symbol)
322+
val highlighted = (formattedTypeDefs ++ formattedMembers)
323+
.map(d => new Diagnostic(d.msg.mapMsg(SyntaxHighlighting.highlight), d.pos, d.level))
324+
(newState, highlighted)
318325
}
319326
.getOrElse {
320327
// user defined a trait/class/object, so no module needed
321-
state
328+
(state, Seq.empty)
322329
}
323330
}
324331
}
@@ -378,18 +385,9 @@ class ReplDriver(settings: Array[String],
378385
state
379386
}
380387

381-
/** A `MessageRenderer` without file positions */
382-
private val messageRenderer = new MessageRendering {
383-
override def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context): String = ""
384-
}
385-
386-
/** Render messages using the `MessageRendering` trait */
387-
private def renderMessage(dia: Diagnostic): Context => String =
388-
messageRenderer.messageAndPos(dia.msg, dia.pos, messageRenderer.diagnosticLevel(dia))(_)
389-
390-
/** Output errors to `out` */
388+
/** shows all errors nicely formatted */
391389
private def displayErrors(errs: Seq[Diagnostic])(implicit state: State): State = {
392-
errs.map(renderMessage(_)(state.context)).foreach(out.println)
390+
errs.map(rendering.formatError).foreach(out.println)
393391
state
394392
}
395393
}
Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
package dotty.tools
22

3-
import dotc.core.Contexts.Context
4-
import dotc.core.Symbols.Symbol
5-
import dotc.printing.ReplPrinter
63
import dotc.reporting.{HideNonSensicalMessages, StoreReporter, UniqueMessagePositions}
74

85
package object repl {
96
/** Create empty outer store reporter */
107
private[repl] def newStoreReporter: StoreReporter =
118
new StoreReporter(null)
129
with UniqueMessagePositions with HideNonSensicalMessages
13-
14-
private[repl] implicit class ShowUser(val s: Symbol) extends AnyVal {
15-
def showUser(implicit ctx: Context): String = {
16-
val printer = new ReplPrinter(ctx)
17-
val text = printer.dclText(s)
18-
text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value)
19-
}
20-
}
2110
}

0 commit comments

Comments
 (0)