Skip to content

Commit 9da40cb

Browse files
authored
Merge pull request #1345 from dotty-staging/improve-Xprint
Improve Xprint
2 parents dfa3280 + f359953 commit 9da40cb

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
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: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ package dotty.tools
22
package dotc
33

44
import core._
5-
import Contexts._, Periods._, Symbols._, Phases._, Decorators._
5+
import Contexts._
6+
import Periods._
7+
import Symbols._
8+
import Phases._
9+
import Decorators._
610
import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer
711
import io.PlainFile
8-
import util.{SourceFile, NoSource, Stats, SimpleMap}
12+
import util._
913
import reporting.Reporter
1014
import transform.TreeChecker
1115
import rewrite.Rewrites
1216
import java.io.{BufferedWriter, OutputStreamWriter}
17+
18+
import scala.annotation.tailrec
1319
import scala.reflect.io.VirtualFile
1420
import scala.util.control.NonFatal
1521

@@ -56,26 +62,48 @@ class Run(comp: Compiler)(implicit ctx: Context) {
5662
val phases = ctx.squashPhases(ctx.phasePlan,
5763
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
5864
ctx.usePhases(phases)
65+
var lastPrintedTree: PrintedTree = NoPrintedTree
5966
for (phase <- ctx.allPhases)
6067
if (!ctx.reporter.hasErrors) {
6168
val start = System.currentTimeMillis
6269
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)
70+
if (ctx.settings.Xprint.value.containsPhase(phase)) {
71+
for (unit <- units) {
72+
lastPrintedTree =
73+
printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
74+
}
75+
}
6776
ctx.informTime(s"$phase ", start)
6877
}
6978
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
7079
}
7180

72-
private def printTree(ctx: Context) = {
81+
private sealed trait PrintedTree
82+
private final case class SomePrintedTree(phase: String, tree: String) extends PrintedTree
83+
private object NoPrintedTree extends PrintedTree
84+
85+
private def printTree(last: PrintedTree)(implicit ctx: Context): PrintedTree = {
7386
val unit = ctx.compilationUnit
7487
val prevPhase = ctx.phase.prev // can be a mini-phase
7588
val squashedPhase = ctx.squashed(prevPhase)
89+
val treeString = unit.tpdTree.show
90+
91+
ctx.echo(s"result of $unit after $squashedPhase:")
7692

77-
ctx.echo(s"result of $unit after ${squashedPhase}:")
78-
ctx.echo(unit.tpdTree.show(ctx))
93+
last match {
94+
case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString =>
95+
val diff = DiffUtil.mkColoredCodeDiff(treeString, lastTreeSting, ctx.settings.XprintDiffDel.value)
96+
ctx.echo(diff)
97+
SomePrintedTree(squashedPhase.toString, treeString)
98+
99+
case SomePrintedTree(phase, lastTreeSting) =>
100+
ctx.echo(" Unchanged since " + phase)
101+
last
102+
103+
case NoPrintedTree =>
104+
ctx.echo(treeString)
105+
SomePrintedTree(squashedPhase.toString, treeString)
106+
}
79107
}
80108

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

src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class ScalaSettings extends Settings.SettingGroup {
8585
val writeICode = PhasesSetting("-Xprint-icode", "Log internal icode to *.icode files after", "icode")
8686
val Xprintpos = BooleanSetting("-Xprint-pos", "Print tree positions, as offsets.")
8787
val printtypes = BooleanSetting("-Xprint-types", "Print tree types (debugging option).")
88+
val XprintDiffDel = BooleanSetting("-Xprint-diff-del", "Print deleted parts of the tree since last print.")
8889
val prompt = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).")
8990
val script = StringSetting("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "")
9091
val mainClass = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package dotty.tools.dotc.util
2+
3+
import scala.annotation.tailrec
4+
import difflib._
5+
6+
object DiffUtil {
7+
8+
private final val ANSI_DEFAULT = "\u001B[0m"
9+
private final val ANSI_RED = "\u001B[31m"
10+
private final val ANSI_GREEN = "\u001B[32m"
11+
private final val ANSI_YELLOW = "\u001B[33m"
12+
private final val ANSI_MAGENTA = "\u001B[35m"
13+
14+
def mkColoredCodeDiff(code: String, lastCode: String, printDiffDel: Boolean): String = {
15+
import scala.collection.JavaConversions._
16+
17+
@tailrec def split(str: String, acc: List[String]): List[String] = {
18+
if (str == "") {
19+
acc.reverse
20+
} else {
21+
val head = str.charAt(0)
22+
val (token, rest) = if (Character.isAlphabetic(head) || Character.isDigit(head)) {
23+
str.span(c => Character.isAlphabetic(c) || Character.isDigit(c))
24+
} else if (Character.isMirrored(head) || Character.isWhitespace(head)) {
25+
str.splitAt(1)
26+
} else {
27+
str.span { c =>
28+
!Character.isAlphabetic(c) && !Character.isDigit(c) &&
29+
!Character.isMirrored(c) && !Character.isWhitespace(c)
30+
}
31+
}
32+
split(rest, token :: acc)
33+
}
34+
}
35+
36+
val lines = split(code, Nil).toArray
37+
val diff = DiffUtils.diff(split(lastCode, Nil), lines.toList)
38+
39+
for (delta <- diff.getDeltas) {
40+
val pos = delta.getRevised.getPosition
41+
val endPos = pos + delta.getRevised.getLines.size - 1
42+
43+
delta.getType.toString match { // Issue #1355 forces us to use the toString
44+
case "INSERT" =>
45+
lines(pos) = ANSI_GREEN + lines(pos)
46+
lines(endPos) = lines(endPos) + ANSI_DEFAULT
47+
48+
case "CHANGE" =>
49+
val old = if (!printDiffDel) "" else
50+
ANSI_MAGENTA + delta.getOriginal.getLines.mkString + ANSI_DEFAULT
51+
lines(pos) = old + ANSI_YELLOW + lines(pos)
52+
lines(endPos) = lines(endPos) + ANSI_DEFAULT
53+
54+
case "DELETE" if printDiffDel =>
55+
val deleted = delta.getOriginal.getLines.mkString
56+
if (!deleted.forall(Character.isWhitespace)) {
57+
lines(pos) = ANSI_RED + deleted + ANSI_DEFAULT + lines(pos)
58+
}
59+
60+
case _ =>
61+
}
62+
}
63+
64+
lines.mkString
65+
}
66+
}

0 commit comments

Comments
 (0)