Skip to content

Commit 91e2da6

Browse files
committed
Improve Xprint
* Do not reprint a tree that has not changed. * Highlight changes in yellow.
1 parent b35eff9 commit 91e2da6

File tree

2 files changed

+76
-7
lines changed

2 files changed

+76
-7
lines changed

project/Build.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ object DottyBuild extends Build {
100100
"org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
101101
"com.novocode" % "junit-interface" % "0.11" % "test",
102102
"jline" % "jline" % "2.12",
103+
"com.googlecode.java-diff-utils" % "diffutils" % "1.3.0",
103104
"com.typesafe.sbt" % "sbt-interface" % sbtVersion.value),
104105
// enable improved incremental compilation algorithm
105106
incOptions := incOptions.value.withNameHashing(true),

src/dotty/tools/dotc/Run.scala

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ import reporting.Reporter
1010
import transform.TreeChecker
1111
import rewrite.Rewrites
1212
import java.io.{BufferedWriter, OutputStreamWriter}
13+
14+
import scala.annotation.tailrec
1315
import scala.reflect.io.VirtualFile
1416
import scala.util.control.NonFatal
1517

1618
/** A compiler run. Exports various methods to compile source files */
1719
class Run(comp: Compiler)(implicit ctx: Context) {
1820

21+
private final val ANSI_DEFAULT = "\u001B[0m"
22+
private final val ANSI_YELLOW = "\u001B[33m"
23+
1924
assert(comp.phases.last.last.id <= Periods.MaxPossiblePhaseId)
2025
assert(ctx.runId <= Periods.MaxPossibleRunId)
2126

@@ -56,26 +61,89 @@ class Run(comp: Compiler)(implicit ctx: Context) {
5661
val phases = ctx.squashPhases(ctx.phasePlan,
5762
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
5863
ctx.usePhases(phases)
64+
var lastPrintedTree: PrintedTree = NoPrintedTree
5965
for (phase <- ctx.allPhases)
6066
if (!ctx.reporter.hasErrors) {
6167
val start = System.currentTimeMillis
6268
units = phase.runOn(units)
63-
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
64-
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
65-
if (ctx.settings.Xprint.value.containsPhase(phase))
66-
foreachUnit(printTree)
69+
if (ctx.settings.Xprint.value.containsPhase(phase)) {
70+
for (unit <- units) {
71+
lastPrintedTree =
72+
printTree(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit), lastPrintedTree)
73+
}
74+
}
6775
ctx.informTime(s"$phase ", start)
6876
}
6977
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
7078
}
7179

72-
private def printTree(ctx: Context) = {
80+
private sealed trait PrintedTree
81+
private final case class SomePrintedTree(phase: String, tree: String) extends PrintedTree
82+
private object NoPrintedTree extends PrintedTree
83+
84+
private def printTree(ctx: Context, last: PrintedTree): PrintedTree = {
7385
val unit = ctx.compilationUnit
7486
val prevPhase = ctx.phase.prev // can be a mini-phase
7587
val squashedPhase = ctx.squashed(prevPhase)
88+
val treeString = unit.tpdTree.show(ctx)
89+
90+
ctx.echo(s"result of $unit after $squashedPhase:")
91+
92+
last match {
93+
case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString =>
94+
ctx.echo(mkColoredDiffTree(treeString, lastTreeSting))
95+
SomePrintedTree(squashedPhase.toString, treeString)
96+
97+
case SomePrintedTree(phase, lastTreeSting) =>
98+
ctx.echo(" Unchanged since " + phase)
99+
last
100+
101+
case NoPrintedTree =>
102+
ctx.echo(treeString)
103+
SomePrintedTree(squashedPhase.toString, treeString)
104+
}
105+
}
106+
107+
private def mkColoredDiffTree(treeString: String, lastTreeSting: String): String = {
108+
import difflib._
109+
import scala.collection.JavaConversions._
110+
111+
@tailrec def split(str: String, acc: List[String]): List[String] = {
112+
if (str == "") {
113+
acc.reverse
114+
} else {
115+
val head = str.charAt(0)
116+
val (token, rest) = if (Character.isAlphabetic(head) || Character.isDigit(head)) {
117+
str.span(c => Character.isAlphabetic(c) || Character.isDigit(c))
118+
} else if (Character.isMirrored(head) || Character.isWhitespace(head)) {
119+
str.splitAt(1)
120+
} else {
121+
str.span { c =>
122+
!Character.isAlphabetic(c) && !Character.isDigit(c) &&
123+
!Character.isMirrored(c) && !Character.isWhitespace(c)
124+
}
125+
}
126+
split(rest, token :: acc)
127+
}
128+
}
129+
130+
val lines = split(treeString, Nil).toArray
131+
132+
val diff = DiffUtils.diff(split(lastTreeSting, Nil), lines.toList)
133+
for (delta <- diff.getDeltas) {
134+
val pos = delta.getRevised.getPosition
135+
val endPos = pos + delta.getRevised.getLines.size - 1
136+
137+
delta.getType match {
138+
case Delta.TYPE.CHANGE | Delta.TYPE.INSERT =>
139+
lines(pos) = ANSI_YELLOW + lines(pos)
140+
lines(endPos) = lines(endPos) + ANSI_DEFAULT
141+
142+
case Delta.TYPE.DELETE =>
143+
}
144+
}
76145

77-
ctx.echo(s"result of $unit after ${squashedPhase}:")
78-
ctx.echo(unit.tpdTree.show(ctx))
146+
lines.mkString
79147
}
80148

81149
def compile(sourceCode: String): Unit = {

0 commit comments

Comments
 (0)